mesa: include stdio.h where needed
[mesa.git] / src / mesa / main / texobj.c
index 334dee77b91e1e7aff56aa408f75f5d800b096fd..e018ab9103c2c18149b7b40bc8dc42b5a57d44ca 100644 (file)
@@ -28,6 +28,7 @@
  */
 
 
+#include <stdio.h>
 #include "bufferobj.h"
 #include "colortab.h"
 #include "context.h"
 /** \name Internal functions */
 /*@{*/
 
+/**
+ * This function checks for all valid combinations of Min and Mag filters for
+ * Float types, when extensions like OES_texture_float and
+ * OES_texture_float_linear are supported. OES_texture_float mentions support
+ * for NEAREST, NEAREST_MIPMAP_NEAREST magnification and minification filters.
+ * Mag filters like LINEAR and min filters like NEAREST_MIPMAP_LINEAR,
+ * LINEAR_MIPMAP_NEAREST and LINEAR_MIPMAP_LINEAR are only valid in case
+ * OES_texture_float_linear is supported.
+ *
+ * Returns true in case the filter is valid for given Float type else false.
+ */
+static bool
+valid_filter_for_float(const struct gl_context *ctx,
+                       const struct gl_texture_object *obj)
+{
+   switch (obj->Sampler.MagFilter) {
+   case GL_LINEAR:
+      if (obj->_IsHalfFloat && !ctx->Extensions.OES_texture_half_float_linear) {
+         return false;
+      } else if (obj->_IsFloat && !ctx->Extensions.OES_texture_float_linear) {
+         return false;
+      }
+   case GL_NEAREST:
+   case GL_NEAREST_MIPMAP_NEAREST:
+      break;
+   default:
+      unreachable("Invalid mag filter");
+   }
+
+   switch (obj->Sampler.MinFilter) {
+   case GL_LINEAR:
+   case GL_NEAREST_MIPMAP_LINEAR:
+   case GL_LINEAR_MIPMAP_NEAREST:
+   case GL_LINEAR_MIPMAP_LINEAR:
+      if (obj->_IsHalfFloat && !ctx->Extensions.OES_texture_half_float_linear) {
+         return false;
+      } else if (obj->_IsFloat && !ctx->Extensions.OES_texture_float_linear) {
+         return false;
+      }
+   case GL_NEAREST:
+   case GL_NEAREST_MIPMAP_NEAREST:
+      break;
+   default:
+      unreachable("Invalid min filter");
+   }
+
+   return true;
+}
 
 /**
  * Return the gl_texture_object for a given ID.
@@ -60,6 +109,124 @@ _mesa_lookup_texture(struct gl_context *ctx, GLuint id)
       _mesa_HashLookup(ctx->Shared->TexObjects, id);
 }
 
+/**
+ * Wrapper around _mesa_lookup_texture that throws GL_INVALID_OPERATION if id
+ * is not in the hash table. After calling _mesa_error, it returns NULL.
+ */
+struct gl_texture_object *
+_mesa_lookup_texture_err(struct gl_context *ctx, GLuint id, const char* func)
+{
+   struct gl_texture_object *texObj;
+
+   texObj = _mesa_lookup_texture(ctx, id); /* Returns NULL if not found. */
+
+   if (!texObj)
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(texture)", func);
+
+   return texObj;
+}
+
+void
+_mesa_begin_texture_lookups(struct gl_context *ctx)
+{
+   _mesa_HashLockMutex(ctx->Shared->TexObjects);
+}
+
+
+void
+_mesa_end_texture_lookups(struct gl_context *ctx)
+{
+   _mesa_HashUnlockMutex(ctx->Shared->TexObjects);
+}
+
+
+struct gl_texture_object *
+_mesa_lookup_texture_locked(struct gl_context *ctx, GLuint id)
+{
+   return (struct gl_texture_object *)
+      _mesa_HashLookupLocked(ctx->Shared->TexObjects, id);
+}
+
+/**
+ * Return a pointer to the current texture object for the given target
+ * on the current texture unit.
+ * Note: all <target> error checking should have been done by this point.
+ */
+struct gl_texture_object *
+_mesa_get_current_tex_object(struct gl_context *ctx, GLenum target)
+{
+   struct gl_texture_unit *texUnit = _mesa_get_current_tex_unit(ctx);
+   const GLboolean arrayTex = ctx->Extensions.EXT_texture_array;
+
+   switch (target) {
+      case GL_TEXTURE_1D:
+         return texUnit->CurrentTex[TEXTURE_1D_INDEX];
+      case GL_PROXY_TEXTURE_1D:
+         return ctx->Texture.ProxyTex[TEXTURE_1D_INDEX];
+      case GL_TEXTURE_2D:
+         return texUnit->CurrentTex[TEXTURE_2D_INDEX];
+      case GL_PROXY_TEXTURE_2D:
+         return ctx->Texture.ProxyTex[TEXTURE_2D_INDEX];
+      case GL_TEXTURE_3D:
+         return texUnit->CurrentTex[TEXTURE_3D_INDEX];
+      case GL_PROXY_TEXTURE_3D:
+         return ctx->Texture.ProxyTex[TEXTURE_3D_INDEX];
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB:
+      case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB:
+      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB:
+      case GL_TEXTURE_CUBE_MAP_ARB:
+         return ctx->Extensions.ARB_texture_cube_map
+                ? texUnit->CurrentTex[TEXTURE_CUBE_INDEX] : NULL;
+      case GL_PROXY_TEXTURE_CUBE_MAP_ARB:
+         return ctx->Extensions.ARB_texture_cube_map
+                ? ctx->Texture.ProxyTex[TEXTURE_CUBE_INDEX] : NULL;
+      case GL_TEXTURE_CUBE_MAP_ARRAY:
+         return ctx->Extensions.ARB_texture_cube_map_array
+                ? texUnit->CurrentTex[TEXTURE_CUBE_ARRAY_INDEX] : NULL;
+      case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY:
+         return ctx->Extensions.ARB_texture_cube_map_array
+                ? ctx->Texture.ProxyTex[TEXTURE_CUBE_ARRAY_INDEX] : NULL;
+      case GL_TEXTURE_RECTANGLE_NV:
+         return ctx->Extensions.NV_texture_rectangle
+                ? texUnit->CurrentTex[TEXTURE_RECT_INDEX] : NULL;
+      case GL_PROXY_TEXTURE_RECTANGLE_NV:
+         return ctx->Extensions.NV_texture_rectangle
+                ? ctx->Texture.ProxyTex[TEXTURE_RECT_INDEX] : NULL;
+      case GL_TEXTURE_1D_ARRAY_EXT:
+         return arrayTex ? texUnit->CurrentTex[TEXTURE_1D_ARRAY_INDEX] : NULL;
+      case GL_PROXY_TEXTURE_1D_ARRAY_EXT:
+         return arrayTex ? ctx->Texture.ProxyTex[TEXTURE_1D_ARRAY_INDEX] : NULL;
+      case GL_TEXTURE_2D_ARRAY_EXT:
+         return arrayTex ? texUnit->CurrentTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
+      case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
+         return arrayTex ? ctx->Texture.ProxyTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
+      case GL_TEXTURE_BUFFER:
+         return ctx->API == API_OPENGL_CORE &&
+                ctx->Extensions.ARB_texture_buffer_object ?
+                texUnit->CurrentTex[TEXTURE_BUFFER_INDEX] : NULL;
+      case GL_TEXTURE_EXTERNAL_OES:
+         return _mesa_is_gles(ctx) && ctx->Extensions.OES_EGL_image_external
+            ? texUnit->CurrentTex[TEXTURE_EXTERNAL_INDEX] : NULL;
+      case GL_TEXTURE_2D_MULTISAMPLE:
+         return ctx->Extensions.ARB_texture_multisample
+            ? texUnit->CurrentTex[TEXTURE_2D_MULTISAMPLE_INDEX] : NULL;
+      case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
+         return ctx->Extensions.ARB_texture_multisample
+            ? ctx->Texture.ProxyTex[TEXTURE_2D_MULTISAMPLE_INDEX] : NULL;
+      case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+         return ctx->Extensions.ARB_texture_multisample
+            ? texUnit->CurrentTex[TEXTURE_2D_MULTISAMPLE_ARRAY_INDEX] : NULL;
+      case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
+         return ctx->Extensions.ARB_texture_multisample
+            ? ctx->Texture.ProxyTex[TEXTURE_2D_MULTISAMPLE_ARRAY_INDEX] : NULL;
+      default:
+         _mesa_problem(NULL, "bad target in _mesa_get_current_tex_object()");
+         return NULL;
+   }
+}
 
 
 /**
@@ -68,7 +235,7 @@ _mesa_lookup_texture(struct gl_context *ctx, GLuint id)
  *
  * Called via ctx->Driver.NewTextureObject, unless overridden by a device
  * driver.
- * 
+ *
  * \param shared the shared GL state structure to contain the texture object
  * \param name integer name for the texture object
  * \param target either GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D,
@@ -99,7 +266,7 @@ _mesa_initialize_texture_object( struct gl_context *ctx,
                                  struct gl_texture_object *obj,
                                  GLuint name, GLenum target )
 {
-   ASSERT(target == 0 ||
+   assert(target == 0 ||
           target == GL_TEXTURE_1D ||
           target == GL_TEXTURE_2D ||
           target == GL_TEXTURE_3D ||
@@ -115,7 +282,7 @@ _mesa_initialize_texture_object( struct gl_context *ctx,
 
    memset(obj, 0, sizeof(*obj));
    /* init the non-zero fields */
-   _glthread_INIT_MUTEX(obj->Mutex);
+   mtx_init(&obj->Mutex, mtx_plain);
    obj->RefCount = 1;
    obj->Name = name;
    obj->Target = target;
@@ -148,6 +315,7 @@ _mesa_initialize_texture_object( struct gl_context *ctx,
    obj->Sampler.CompareMode = GL_NONE;         /* ARB_shadow */
    obj->Sampler.CompareFunc = GL_LEQUAL;       /* ARB_shadow */
    obj->DepthMode = ctx->API == API_OPENGL_CORE ? GL_RED : GL_LUMINANCE;
+   obj->StencilSampling = false;
    obj->Sampler.CubeMapSeamless = GL_FALSE;
    obj->Swizzle[0] = GL_RED;
    obj->Swizzle[1] = GL_GREEN;
@@ -156,7 +324,8 @@ _mesa_initialize_texture_object( struct gl_context *ctx,
    obj->_Swizzle = SWIZZLE_NOOP;
    obj->Sampler.sRGBDecode = GL_DECODE_EXT;
    obj->BufferObjectFormat = GL_R8;
-   obj->_BufferObjectFormat = MESA_FORMAT_R8;
+   obj->_BufferObjectFormat = MESA_FORMAT_R_UNORM8;
+   obj->ImageFormatCompatibilityType = GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE;
 }
 
 
@@ -188,12 +357,12 @@ finish_texture_init(struct gl_context *ctx, GLenum target,
          if (ctx->Driver.TexParameter) {
             static const GLfloat fparam_wrap[1] = {(GLfloat) GL_CLAMP_TO_EDGE};
             const GLfloat fparam_filter[1] = {(GLfloat) filter};
-            ctx->Driver.TexParameter(ctx, target, obj, GL_TEXTURE_WRAP_S, fparam_wrap);
-            ctx->Driver.TexParameter(ctx, target, obj, GL_TEXTURE_WRAP_T, fparam_wrap);
-            ctx->Driver.TexParameter(ctx, target, obj, GL_TEXTURE_WRAP_R, fparam_wrap);
-            ctx->Driver.TexParameter(ctx, target, obj,
+            ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_S, fparam_wrap);
+            ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_T, fparam_wrap);
+            ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_R, fparam_wrap);
+            ctx->Driver.TexParameter(ctx, obj,
                   GL_TEXTURE_MIN_FILTER, fparam_filter);
-            ctx->Driver.TexParameter(ctx, target, obj,
+            ctx->Driver.TexParameter(ctx, obj,
                   GL_TEXTURE_MAG_FILTER, fparam_filter);
          }
          break;
@@ -236,14 +405,15 @@ _mesa_delete_texture_object(struct gl_context *ctx,
    _mesa_reference_buffer_object(ctx, &texObj->BufferObject, NULL);
 
    /* destroy the mutex -- it may have allocated memory (eg on bsd) */
-   _glthread_DESTROY_MUTEX(texObj->Mutex);
+   mtx_destroy(&texObj->Mutex);
+
+   free(texObj->Label);
 
    /* free this object */
    free(texObj);
 }
 
 
-
 /**
  * Copy texture object state from one texture object to another.
  * Use for glPush/PopAttrib.
@@ -256,6 +426,7 @@ _mesa_copy_texture_object( struct gl_texture_object *dest,
                            const struct gl_texture_object *src )
 {
    dest->Target = src->Target;
+   dest->TargetIndex = src->TargetIndex;
    dest->Name = src->Name;
    dest->Priority = src->Priority;
    dest->Sampler.BorderColor.f[0] = src->Sampler.BorderColor.f[0];
@@ -277,6 +448,7 @@ _mesa_copy_texture_object( struct gl_texture_object *dest,
    dest->Sampler.CompareFunc = src->Sampler.CompareFunc;
    dest->Sampler.CubeMapSeamless = src->Sampler.CubeMapSeamless;
    dest->DepthMode = src->DepthMode;
+   dest->StencilSampling = src->StencilSampling;
    dest->Sampler.sRGBDecode = src->Sampler.sRGBDecode;
    dest->_MaxLevel = src->_MaxLevel;
    dest->_MaxLambda = src->_MaxLambda;
@@ -285,6 +457,8 @@ _mesa_copy_texture_object( struct gl_texture_object *dest,
    dest->_MipmapComplete = src->_MipmapComplete;
    COPY_4V(dest->Swizzle, src->Swizzle);
    dest->_Swizzle = src->_Swizzle;
+   dest->_IsHalfFloat = src->_IsHalfFloat;
+   dest->_IsFloat = src->_IsFloat;
 
    dest->RequiredTextureImageUnits = src->RequiredTextureImageUnits;
 }
@@ -368,17 +542,20 @@ _mesa_reference_texobj_(struct gl_texture_object **ptr,
       GLboolean deleteFlag = GL_FALSE;
       struct gl_texture_object *oldTex = *ptr;
 
-      ASSERT(valid_texture_object(oldTex));
+      assert(valid_texture_object(oldTex));
       (void) valid_texture_object; /* silence warning in release builds */
 
-      _glthread_LOCK_MUTEX(oldTex->Mutex);
-      ASSERT(oldTex->RefCount > 0);
+      mtx_lock(&oldTex->Mutex);
+      assert(oldTex->RefCount > 0);
       oldTex->RefCount--;
 
       deleteFlag = (oldTex->RefCount == 0);
-      _glthread_UNLOCK_MUTEX(oldTex->Mutex);
+      mtx_unlock(&oldTex->Mutex);
 
       if (deleteFlag) {
+         /* Passing in the context drastically changes the driver code for
+          * framebuffer deletion.
+          */
          GET_CURRENT_CONTEXT(ctx);
          if (ctx)
             ctx->Driver.DeleteTexture(ctx, oldTex);
@@ -392,8 +569,8 @@ _mesa_reference_texobj_(struct gl_texture_object **ptr,
 
    if (tex) {
       /* reference new texture */
-      ASSERT(valid_texture_object(tex));
-      _glthread_LOCK_MUTEX(tex->Mutex);
+      assert(valid_texture_object(tex));
+      mtx_lock(&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. */
@@ -404,7 +581,7 @@ _mesa_reference_texobj_(struct gl_texture_object **ptr,
          tex->RefCount++;
          *ptr = tex;
       }
-      _glthread_UNLOCK_MUTEX(tex->Mutex);
+      mtx_unlock(&tex->Mutex);
    }
 }
 
@@ -515,6 +692,14 @@ _mesa_test_texobj_completeness( const struct gl_context *ctx,
       t->_IsIntegerFormat = datatype == GL_INT || datatype == GL_UNSIGNED_INT;
    }
 
+   /* Check if the texture type is Float or HalfFloatOES and ensure Min and Mag
+    * filters are supported in this case.
+    */
+   if (_mesa_is_gles(ctx) && !valid_filter_for_float(ctx, t)) {
+      incomplete(t, BASE, "Filter is not supported with Float types.");
+      return;
+   }
+
    /* Compute _MaxLevel (the maximum mipmap level we'll sample from given the
     * mipmap image sizes and GL_TEXTURE_MAX_LEVEL state).
     */
@@ -546,14 +731,22 @@ _mesa_test_texobj_completeness( const struct gl_context *ctx,
       return;
    }
 
-   ASSERT(maxLevels > 0);
+   assert(maxLevels > 0);
 
-   t->_MaxLevel =
-      baseLevel + baseImage->MaxNumLevels - 1; /* 'p' in the GL spec */
-   t->_MaxLevel = MIN2(t->_MaxLevel, t->MaxLevel);
-   t->_MaxLevel = MIN2(t->_MaxLevel, maxLevels - 1); /* 'q' in the GL spec */
+   t->_MaxLevel = MIN3(t->MaxLevel,
+                       /* 'p' in the GL spec */
+                       (int) (baseLevel + baseImage->MaxNumLevels - 1),
+                       /* 'q' in the GL spec */
+                       maxLevels - 1);
 
-   /* Compute _MaxLambda = q - b (see the 1.2 spec) used during mipmapping */
+   if (t->Immutable) {
+      /* Adjust max level for views: the data store may have more levels than
+       * the view exposes.
+       */
+      t->_MaxLevel = MIN2(t->_MaxLevel, t->NumLevels - 1);
+   }
+
+   /* Compute _MaxLambda = q - p in the spec used during mipmapping */
    t->_MaxLambda = (GLfloat) (t->_MaxLevel - baseLevel);
 
    if (t->Immutable) {
@@ -618,7 +811,8 @@ _mesa_test_texobj_completeness( const struct gl_context *ctx,
          if (height > 1 && t->Target != GL_TEXTURE_1D_ARRAY) {
             height /= 2;
          }
-         if (depth > 1 && t->Target != GL_TEXTURE_2D_ARRAY && t->Target != GL_TEXTURE_CUBE_MAP_ARRAY) {
+         if (depth > 1 && t->Target != GL_TEXTURE_2D_ARRAY
+             && t->Target != GL_TEXTURE_CUBE_MAP_ARRAY) {
             depth /= 2;
          }
 
@@ -640,22 +834,25 @@ _mesa_test_texobj_completeness( const struct gl_context *ctx,
                   return;
                }
                if (img->Width2 != width) {
-                  incomplete(t, MIPMAP, "TexImage[%d] bad width %u", i, img->Width2);
+                  incomplete(t, MIPMAP, "TexImage[%d] bad width %u", i,
+                             img->Width2);
                   return;
                }
                if (img->Height2 != height) {
-                  incomplete(t, MIPMAP, "TexImage[%d] bad height %u", i, img->Height2);
+                  incomplete(t, MIPMAP, "TexImage[%d] bad height %u", i,
+                             img->Height2);
                   return;
                }
                if (img->Depth2 != depth) {
-                  incomplete(t, MIPMAP, "TexImage[%d] bad depth %u", i, img->Depth2);
+                  incomplete(t, MIPMAP, "TexImage[%d] bad depth %u", i,
+                             img->Depth2);
                   return;
                }
 
                /* Extra checks for cube textures */
                if (face > 0) {
                   /* check that cube faces are the same size */
-                  if (img->Width2 != t->Image[0][i]->Width2 || 
+                  if (img->Width2 != t->Image[0][i]->Width2 ||
                       img->Height2 != t->Image[0][i]->Height2) {
                     incomplete(t, MIPMAP, "CubeMap Image[n][i] bad size");
                     return;
@@ -663,7 +860,7 @@ _mesa_test_texobj_completeness( const struct gl_context *ctx,
                }
             }
          }
-         
+
          if (width == 1 && height == 1 && depth == 1) {
             return;  /* found smallest needed mipmap, all done! */
          }
@@ -672,25 +869,21 @@ _mesa_test_texobj_completeness( const struct gl_context *ctx,
 }
 
 
-/**
- * Check if the given cube map texture is "cube complete" as defined in
- * the OpenGL specification.
- */
 GLboolean
-_mesa_cube_complete(const struct gl_texture_object *texObj)
+_mesa_cube_level_complete(const struct gl_texture_object *texObj,
+                          const GLint level)
 {
-   const GLint baseLevel = texObj->BaseLevel;
    const struct gl_texture_image *img0, *img;
    GLuint face;
 
    if (texObj->Target != GL_TEXTURE_CUBE_MAP)
       return GL_FALSE;
 
-   if ((baseLevel < 0) || (baseLevel >= MAX_TEXTURE_LEVELS))
+   if ((level < 0) || (level >= MAX_TEXTURE_LEVELS))
       return GL_FALSE;
 
    /* check first face */
-   img0 = texObj->Image[0][baseLevel];
+   img0 = texObj->Image[0][level];
    if (!img0 ||
        img0->Width < 1 ||
        img0->Width != img0->Height)
@@ -698,7 +891,7 @@ _mesa_cube_complete(const struct gl_texture_object *texObj)
 
    /* check remaining faces vs. first face */
    for (face = 1; face < 6; face++) {
-      img = texObj->Image[face][baseLevel];
+      img = texObj->Image[face][level];
       if (!img ||
           img->Width != img0->Width ||
           img->Height != img0->Height ||
@@ -709,23 +902,29 @@ _mesa_cube_complete(const struct gl_texture_object *texObj)
    return GL_TRUE;
 }
 
+/**
+ * Check if the given cube map texture is "cube complete" as defined in
+ * the OpenGL specification.
+ */
+GLboolean
+_mesa_cube_complete(const struct gl_texture_object *texObj)
+{
+   return _mesa_cube_level_complete(texObj, texObj->BaseLevel);
+}
 
 /**
  * Mark a texture object dirty.  It forces the object to be incomplete
- * and optionally forces the context to re-validate its state.
+ * and forces the context to re-validate its state.
  *
  * \param ctx GL context.
  * \param texObj texture object.
- * \param invalidate_state also invalidate context state.
  */
 void
-_mesa_dirty_texobj(struct gl_context *ctx, struct gl_texture_object *texObj,
-                   GLboolean invalidate_state)
+_mesa_dirty_texobj(struct gl_context *ctx, struct gl_texture_object *texObj)
 {
    texObj->_BaseComplete = GL_FALSE;
    texObj->_MipmapComplete = GL_FALSE;
-   if (invalidate_state)
-      ctx->NewState |= _NEW_TEXTURE;
+   ctx->NewState |= _NEW_TEXTURE;
 }
 
 
@@ -740,18 +939,21 @@ _mesa_get_fallback_texture(struct gl_context *ctx, gl_texture_index tex)
 {
    if (!ctx->Shared->FallbackTex[tex]) {
       /* create fallback texture now */
-      const GLsizei width = 1, height = 1, depth = 1;
-      GLubyte texel[4];
+      const GLsizei width = 1, height = 1;
+      GLsizei depth = 1;
+      GLubyte texel[24];
       struct gl_texture_object *texObj;
       struct gl_texture_image *texImage;
-      gl_format texFormat;
+      mesa_format texFormat;
       GLuint dims, face, numFaces = 1;
       GLenum target;
 
-      texel[0] =
-      texel[1] =
-      texel[2] = 0x0;
-      texel[3] = 0xff;
+      for (face = 0; face < 6; face++) {
+         texel[4*face + 0] =
+         texel[4*face + 1] =
+         texel[4*face + 2] = 0x0;
+         texel[4*face + 3] = 0xff;
+      }
 
       switch (tex) {
       case TEXTURE_2D_ARRAY_INDEX:
@@ -790,6 +992,7 @@ _mesa_get_fallback_texture(struct gl_context *ctx, gl_texture_index tex)
       case TEXTURE_CUBE_ARRAY_INDEX:
          dims = 3;
          target = GL_TEXTURE_CUBE_MAP_ARRAY;
+         depth = 6;
          break;
       case TEXTURE_EXTERNAL_INDEX:
          dims = 2;
@@ -889,6 +1092,8 @@ count_tex_size(GLuint key, void *data, void *userData)
       (const struct gl_texture_object *) data;
    GLuint *total = (GLuint *) userData;
 
+   (void) key;
+
    *total = *total + texture_size(texObj);
 }
 
@@ -912,6 +1117,21 @@ _mesa_total_texture_memory(struct gl_context *ctx)
    return total;
 }
 
+
+/**
+ * Return the base format for the given texture object by looking
+ * at the base texture image.
+ * \return base format (such as GL_RGBA) or GL_NONE if it can't be determined
+ */
+GLenum
+_mesa_texture_base_format(const struct gl_texture_object *texObj)
+{
+   const struct gl_texture_image *texImage = _mesa_base_tex_image(texObj);
+
+   return texImage ? texImage->_BaseFormat : GL_NONE;
+}
+
+
 static struct gl_texture_object *
 invalidate_tex_image_error_check(struct gl_context *ctx, GLuint texture,
                                  GLint level, const char *name)
@@ -965,38 +1185,46 @@ invalidate_tex_image_error_check(struct gl_context *ctx, GLuint texture,
    return t;
 }
 
-/*@}*/
+/**
+ * Wrapper for the driver function. Need this because _mesa_new_texture_object
+ * permits a target of 0 and does not initialize targetIndex.
+ */
+struct gl_texture_object *
+_mesa_create_nameless_texture(struct gl_context *ctx, GLenum target)
+{
+      struct gl_texture_object *texObj = NULL;
+      GLint targetIndex;
 
+      if (target == 0)
+         return texObj;
 
-/***********************************************************************/
-/** \name API functions */
-/*@{*/
+      texObj = ctx->Driver.NewTextureObject(ctx, 0, target);
+      targetIndex = _mesa_tex_target_to_index(ctx, texObj->Target);
+      assert(targetIndex < NUM_TEXTURE_TARGETS);
+      texObj->TargetIndex = targetIndex;
 
+      return texObj;
+}
 
 /**
- * Generate texture names.
- *
- * \param n number of texture names to be generated.
- * \param textures an array in which will hold the generated texture names.
- *
- * \sa glGenTextures().
- *
- * Calls _mesa_HashFindFreeKeyBlock() to find a block of free texture
- * IDs which are stored in \p textures.  Corresponding empty texture
- * objects are also generated.
- */ 
-void GLAPIENTRY
-_mesa_GenTextures( GLsizei n, GLuint *textures )
+ * Helper function for glCreateTextures and glGenTextures. Need this because
+ * glCreateTextures should throw errors if target = 0. This is not exposed to
+ * the rest of Mesa to encourage Mesa internals to use nameless textures,
+ * which do not require expensive hash lookups.
+ */
+static void
+create_textures(struct gl_context *ctx, GLenum target,
+                GLsizei n, GLuint *textures, bool dsa)
 {
-   GET_CURRENT_CONTEXT(ctx);
    GLuint first;
    GLint i;
+   const char *func = dsa ? "Create" : "Gen";
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glGenTextures %d\n", n);
+      _mesa_debug(ctx, "gl%sTextures %d\n", func, n);
 
    if (n < 0) {
-      _mesa_error( ctx, GL_INVALID_VALUE, "glGenTextures" );
+      _mesa_error( ctx, GL_INVALID_VALUE, "gl%sTextures(n < 0)", func );
       return;
    }
 
@@ -1006,31 +1234,103 @@ _mesa_GenTextures( GLsizei n, GLuint *textures )
    /*
     * This must be atomic (generation and allocation of texture IDs)
     */
-   _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+   mtx_lock(&ctx->Shared->Mutex);
 
    first = _mesa_HashFindFreeKeyBlock(ctx->Shared->TexObjects, n);
 
    /* Allocate new, empty texture objects */
    for (i = 0; i < n; i++) {
       struct gl_texture_object *texObj;
+      GLint targetIndex;
       GLuint name = first + i;
-      GLenum target = 0;
       texObj = ctx->Driver.NewTextureObject(ctx, name, target);
       if (!texObj) {
-         _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
-         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTextures");
+         mtx_unlock(&ctx->Shared->Mutex);
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "gl%sTextures", func);
          return;
       }
 
+      /* Initialize the target index if target is non-zero. */
+      if (target != 0) {
+         targetIndex = _mesa_tex_target_to_index(ctx, texObj->Target);
+         if (targetIndex < 0) { /* Bad Target */
+            mtx_unlock(&ctx->Shared->Mutex);
+            _mesa_error(ctx, GL_INVALID_ENUM, "gl%sTextures(target = %s)",
+                        func, _mesa_lookup_enum_by_nr(texObj->Target));
+            return;
+         }
+         assert(targetIndex < NUM_TEXTURE_TARGETS);
+         texObj->TargetIndex = targetIndex;
+      }
+
       /* insert into hash table */
       _mesa_HashInsert(ctx->Shared->TexObjects, texObj->Name, texObj);
 
       textures[i] = name;
    }
 
-   _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+   mtx_unlock(&ctx->Shared->Mutex);
 }
 
+/*@}*/
+
+
+/***********************************************************************/
+/** \name API functions */
+/*@{*/
+
+
+/**
+ * Generate texture names.
+ *
+ * \param n number of texture names to be generated.
+ * \param textures an array in which will hold the generated texture names.
+ *
+ * \sa glGenTextures(), glCreateTextures().
+ *
+ * Calls _mesa_HashFindFreeKeyBlock() to find a block of free texture
+ * IDs which are stored in \p textures.  Corresponding empty texture
+ * objects are also generated.
+ */
+void GLAPIENTRY
+_mesa_GenTextures(GLsizei n, GLuint *textures)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   create_textures(ctx, 0, n, textures, false);
+}
+
+/**
+ * Create texture objects.
+ *
+ * \param target the texture target for each name to be generated.
+ * \param n number of texture names to be generated.
+ * \param textures an array in which will hold the generated texture names.
+ *
+ * \sa glCreateTextures(), glGenTextures().
+ *
+ * Calls _mesa_HashFindFreeKeyBlock() to find a block of free texture
+ * IDs which are stored in \p textures.  Corresponding empty texture
+ * objects are also generated.
+ */
+void GLAPIENTRY
+_mesa_CreateTextures(GLenum target, GLsizei n, GLuint *textures)
+{
+   GLint targetIndex;
+   GET_CURRENT_CONTEXT(ctx);
+
+   /*
+    * The 4.5 core profile spec (30.10.2014) doesn't specify what
+    * glCreateTextures should do with invalid targets, which was probably an
+    * oversight.  This conforms to the spec for glBindTexture.
+    */
+   targetIndex = _mesa_tex_target_to_index(ctx, target);
+   if (targetIndex < 0) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glCreateTextures(target)");
+      return;
+   }
+
+   create_textures(ctx, target, n, textures, true);
+}
 
 /**
  * Check if the given texture object is bound to the current draw or
@@ -1040,23 +1340,35 @@ static void
 unbind_texobj_from_fbo(struct gl_context *ctx,
                        struct gl_texture_object *texObj)
 {
-   const GLuint n = (ctx->DrawBuffer == ctx->ReadBuffer) ? 1 : 2;
-   GLuint i;
+   bool progress = false;
 
-   for (i = 0; i < n; i++) {
-      struct gl_framebuffer *fb = (i == 0) ? ctx->DrawBuffer : ctx->ReadBuffer;
-      if (_mesa_is_user_fbo(fb)) {
-         GLuint j;
-         for (j = 0; j < BUFFER_COUNT; j++) {
-            if (fb->Attachment[j].Type == GL_TEXTURE &&
-                fb->Attachment[j].Texture == texObj) {
-              /* Vertices are already flushed by _mesa_DeleteTextures */
-              ctx->NewState |= _NEW_BUFFERS;
-               _mesa_remove_attachment(ctx, fb->Attachment + j);         
-            }
-         }
-      }
+   /* Section 4.4.2 (Attaching Images to Framebuffer Objects), subsection
+    * "Attaching Texture Images to a Framebuffer," of the OpenGL 3.1 spec
+    * says:
+    *
+    *     "If a texture object is deleted while its image is attached to one
+    *     or more attachment points in the currently bound framebuffer, then
+    *     it is as if FramebufferTexture* had been called, with a texture of
+    *     zero, for each attachment point to which this image was attached in
+    *     the currently bound framebuffer. In other words, this texture image
+    *     is first detached from all attachment points in the currently bound
+    *     framebuffer. Note that the texture image is specifically not
+    *     detached from any other framebuffer objects. Detaching the texture
+    *     image from any other framebuffer objects is the responsibility of
+    *     the application."
+    */
+   if (_mesa_is_user_fbo(ctx->DrawBuffer)) {
+      progress = _mesa_detach_renderbuffer(ctx, ctx->DrawBuffer, texObj);
    }
+   if (_mesa_is_user_fbo(ctx->ReadBuffer)
+       && ctx->ReadBuffer != ctx->DrawBuffer) {
+      progress = _mesa_detach_renderbuffer(ctx, ctx->ReadBuffer, texObj)
+         || progress;
+   }
+
+   if (progress)
+      /* Vertices are already flushed by _mesa_DeleteTextures */
+      ctx->NewState |= _NEW_BUFFERS;
 }
 
 
@@ -1068,22 +1380,68 @@ static void
 unbind_texobj_from_texunits(struct gl_context *ctx,
                             struct gl_texture_object *texObj)
 {
-   GLuint u, tex;
+   const gl_texture_index index = texObj->TargetIndex;
+   GLuint u;
 
-   for (u = 0; u < Elements(ctx->Texture.Unit); u++) {
+   if (texObj->Target == 0)
+      return;
+
+   for (u = 0; u < ctx->Texture.NumCurrentTexUsed; u++) {
       struct gl_texture_unit *unit = &ctx->Texture.Unit[u];
-      for (tex = 0; tex < NUM_TEXTURE_TARGETS; tex++) {
-         if (texObj == unit->CurrentTex[tex]) {
-            _mesa_reference_texobj(&unit->CurrentTex[tex],
-                                   ctx->Shared->DefaultTex[tex]);
-            ASSERT(unit->CurrentTex[tex]);
-            break;
-         }
+
+      if (texObj == unit->CurrentTex[index]) {
+         /* Bind the default texture for this unit/target */
+         _mesa_reference_texobj(&unit->CurrentTex[index],
+                                ctx->Shared->DefaultTex[index]);
+         unit->_BoundTextures &= ~(1 << index);
       }
    }
 }
 
 
+/**
+ * Check if the given texture object is bound to any shader image unit
+ * and unbind it if that's the case.
+ */
+static void
+unbind_texobj_from_image_units(struct gl_context *ctx,
+                               struct gl_texture_object *texObj)
+{
+   GLuint i;
+
+   for (i = 0; i < ctx->Const.MaxImageUnits; i++) {
+      struct gl_image_unit *unit = &ctx->ImageUnits[i];
+
+      if (texObj == unit->TexObj)
+         _mesa_reference_texobj(&unit->TexObj, NULL);
+   }
+}
+
+
+/**
+ * Unbinds all textures bound to the given texture image unit.
+ */
+static void
+unbind_textures_from_unit(struct gl_context *ctx, GLuint unit)
+{
+   struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
+
+   while (texUnit->_BoundTextures) {
+      const GLuint index = ffs(texUnit->_BoundTextures) - 1;
+      struct gl_texture_object *texObj = ctx->Shared->DefaultTex[index];
+
+      _mesa_reference_texobj(&texUnit->CurrentTex[index], texObj);
+
+      /* Pass BindTexture call to device driver */
+      if (ctx->Driver.BindTexture)
+         ctx->Driver.BindTexture(ctx, unit, 0, texObj);
+
+      texUnit->_BoundTextures &= ~(1 << index);
+      ctx->NewState |= _NEW_TEXTURE;
+   }
+}
+
+
 /**
  * Delete named textures.
  *
@@ -1107,8 +1465,18 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
       _mesa_debug(ctx, "glDeleteTextures %d\n", n);
 
+   if (n < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTextures(n < 0)");
+      return;
+   }
+
    FLUSH_VERTICES(ctx, 0); /* too complex */
 
+   if (n < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTextures(n)");
+      return;
+   }
+
    if (!textures)
       return;
 
@@ -1131,6 +1499,12 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
              */
             unbind_texobj_from_texunits(ctx, delObj);
 
+            /* Check if this texture is currently bound to any shader
+             * image unit.  If so, unbind it.
+             * See section 3.9.X of GL_ARB_shader_image_load_store.
+             */
+            unbind_texobj_from_image_units(ctx, delObj);
+
             _mesa_unlock_texture(ctx, delObj);
 
             ctx->NewState |= _NEW_TEXTURE;
@@ -1138,9 +1512,9 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
             /* The texture _name_ is now free for re-use.
              * Remove it from the hash table now.
              */
-            _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+            mtx_lock(&ctx->Shared->Mutex);
             _mesa_HashRemove(ctx->Shared->TexObjects, delObj->Name);
-            _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+            mtx_unlock(&ctx->Shared->Mutex);
 
             /* Unreference the texobj.  If refcount hits zero, the texture
              * will be deleted.
@@ -1151,6 +1525,47 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
    }
 }
 
+/**
+ * This deletes a texObj without altering the hash table.
+ */
+void
+_mesa_delete_nameless_texture(struct gl_context *ctx,
+                              struct gl_texture_object *texObj)
+{
+   if (!texObj)
+      return;
+
+   FLUSH_VERTICES(ctx, 0);
+
+   _mesa_lock_texture(ctx, texObj);
+   {
+      /* Check if texture is bound to any framebuffer objects.
+       * If so, unbind.
+       * See section 4.4.2.3 of GL_EXT_framebuffer_object.
+       */
+      unbind_texobj_from_fbo(ctx, texObj);
+
+      /* Check if this texture is currently bound to any texture units.
+       * If so, unbind it.
+       */
+      unbind_texobj_from_texunits(ctx, texObj);
+
+      /* Check if this texture is currently bound to any shader
+       * image unit.  If so, unbind it.
+       * See section 3.9.X of GL_ARB_shader_image_load_store.
+       */
+      unbind_texobj_from_image_units(ctx, texObj);
+   }
+   _mesa_unlock_texture(ctx, texObj);
+
+   ctx->NewState |= _NEW_TEXTURE;
+
+   /* Unreference the texobj.  If refcount hits zero, the texture
+    * will be deleted.
+    */
+   _mesa_reference_texobj(&texObj, NULL);
+}
+
 
 /**
  * Convert a GL texture target enum such as GL_TEXTURE_2D or GL_TEXTURE_3D
@@ -1158,8 +1573,8 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *textures)
  * Note that proxy targets are not valid here.
  * \return TEXTURE_x_INDEX or -1 if target is invalid
  */
-static GLint
-target_enum_to_index(struct gl_context *ctx, GLenum target)
+int
+_mesa_tex_target_to_index(const struct gl_context *ctx, GLenum target)
 {
    switch (target) {
    case GL_TEXTURE_1D:
@@ -1167,25 +1582,21 @@ target_enum_to_index(struct gl_context *ctx, GLenum target)
    case GL_TEXTURE_2D:
       return TEXTURE_2D_INDEX;
    case GL_TEXTURE_3D:
-      return TEXTURE_3D_INDEX;
-   case GL_TEXTURE_CUBE_MAP_ARB:
+      return ctx->API != API_OPENGLES ? TEXTURE_3D_INDEX : -1;
+   case GL_TEXTURE_CUBE_MAP:
       return ctx->Extensions.ARB_texture_cube_map
          ? TEXTURE_CUBE_INDEX : -1;
-   case GL_TEXTURE_RECTANGLE_NV:
+   case GL_TEXTURE_RECTANGLE:
       return _mesa_is_desktop_gl(ctx) && ctx->Extensions.NV_texture_rectangle
          ? TEXTURE_RECT_INDEX : -1;
-   case GL_TEXTURE_1D_ARRAY_EXT:
-      return _mesa_is_desktop_gl(ctx)
-         && (ctx->Extensions.EXT_texture_array
-             || ctx->Extensions.MESA_texture_array)
+   case GL_TEXTURE_1D_ARRAY:
+      return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_array
          ? TEXTURE_1D_ARRAY_INDEX : -1;
-   case GL_TEXTURE_2D_ARRAY_EXT:
-      return (_mesa_is_desktop_gl(ctx)
-              && (ctx->Extensions.EXT_texture_array
-                  || ctx->Extensions.MESA_texture_array))
+   case GL_TEXTURE_2D_ARRAY:
+      return (_mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_array)
          || _mesa_is_gles3(ctx)
          ? TEXTURE_2D_ARRAY_INDEX : -1;
-   case GL_TEXTURE_BUFFER_ARB:
+   case GL_TEXTURE_BUFFER:
       return ctx->API == API_OPENGL_CORE &&
              ctx->Extensions.ARB_texture_buffer_object ?
              TEXTURE_BUFFER_INDEX : -1;
@@ -1193,7 +1604,8 @@ target_enum_to_index(struct gl_context *ctx, GLenum target)
       return _mesa_is_gles(ctx) && ctx->Extensions.OES_EGL_image_external
          ? TEXTURE_EXTERNAL_INDEX : -1;
    case GL_TEXTURE_CUBE_MAP_ARRAY:
-      return TEXTURE_CUBE_ARRAY_INDEX;
+      return _mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_cube_map_array
+         ? TEXTURE_CUBE_ARRAY_INDEX : -1;
    case GL_TEXTURE_2D_MULTISAMPLE:
       return _mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_multisample
          ? TEXTURE_2D_MULTISAMPLE_INDEX: -1;
@@ -1208,10 +1620,10 @@ target_enum_to_index(struct gl_context *ctx, GLenum target)
 
 /**
  * Bind a named texture to a texturing target.
- * 
+ *
  * \param target texture target.
  * \param texName texture name.
- * 
+ *
  * \sa glBindTexture().
  *
  * Determines the old texture object bound and returns immediately if rebinding
@@ -1233,7 +1645,7 @@ _mesa_BindTexture( GLenum target, GLuint texName )
       _mesa_debug(ctx, "glBindTexture %s %d\n",
                   _mesa_lookup_enum_by_nr(target), (GLint) texName);
 
-   targetIndex = target_enum_to_index(ctx, target);
+   targetIndex = _mesa_tex_target_to_index(ctx, target);
    if (targetIndex < 0) {
       _mesa_error(ctx, GL_INVALID_ENUM, "glBindTexture(target)");
       return;
@@ -1253,7 +1665,9 @@ _mesa_BindTexture( GLenum target, GLuint texName )
       if (newTexObj) {
          /* error checking */
          if (newTexObj->Target != 0 && newTexObj->Target != target) {
-            /* the named texture object's target doesn't match the given target */
+            /* The named texture object's target doesn't match the
+             * given target
+             */
             _mesa_error( ctx, GL_INVALID_OPERATION,
                          "glBindTexture(target mismatch)" );
             return;
@@ -1264,7 +1678,8 @@ _mesa_BindTexture( GLenum target, GLuint texName )
       }
       else {
          if (ctx->API == API_OPENGL_CORE) {
-            _mesa_error(ctx, GL_INVALID_OPERATION, "glBindTexture(non-gen name)");
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glBindTexture(non-gen name)");
             return;
          }
 
@@ -1276,11 +1691,12 @@ _mesa_BindTexture( GLenum target, GLuint texName )
          }
 
          /* and insert it into hash table */
-         _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+         mtx_lock(&ctx->Shared->Mutex);
          _mesa_HashInsert(ctx->Shared->TexObjects, texName, newTexObj);
-         _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+         mtx_unlock(&ctx->Shared->Mutex);
       }
       newTexObj->Target = target;
+      newTexObj->TargetIndex = targetIndex;
    }
 
    assert(valid_texture_object(newTexObj));
@@ -1290,10 +1706,10 @@ _mesa_BindTexture( GLenum target, GLuint texName )
     */
    {
       GLboolean early_out;
-      _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+      mtx_lock(&ctx->Shared->Mutex);
       early_out = ((ctx->Shared->RefCount == 1)
                    && (newTexObj == texUnit->CurrentTex[targetIndex]));
-      _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+      mtx_unlock(&ctx->Shared->Mutex);
       if (early_out) {
          return;
       }
@@ -1307,23 +1723,235 @@ _mesa_BindTexture( GLenum target, GLuint texName )
     * count hits zero.
     */
    _mesa_reference_texobj(&texUnit->CurrentTex[targetIndex], newTexObj);
-   ASSERT(texUnit->CurrentTex[targetIndex]);
+   ctx->Texture.NumCurrentTexUsed = MAX2(ctx->Texture.NumCurrentTexUsed,
+                                         ctx->Texture.CurrentUnit + 1);
+   assert(texUnit->CurrentTex[targetIndex]);
+
+   if (texName != 0)
+      texUnit->_BoundTextures |= (1 << targetIndex);
+   else
+      texUnit->_BoundTextures &= ~(1 << targetIndex);
 
    /* Pass BindTexture call to device driver */
    if (ctx->Driver.BindTexture)
-      ctx->Driver.BindTexture(ctx, target, newTexObj);
+      ctx->Driver.BindTexture(ctx, ctx->Texture.CurrentUnit, target, newTexObj);
+}
+
+/**
+ * Do the actual binding to a numbered texture unit.
+ * The refcount on the previously bound
+ * texture object will be decremented.  It'll be deleted if the
+ * count hits zero.
+ */
+void
+_mesa_bind_texture_unit(struct gl_context *ctx,
+                        GLuint unit,
+                        struct gl_texture_object *texObj)
+{
+   struct gl_texture_unit *texUnit;
+
+   /* Get the texture unit (this is an array look-up) */
+   texUnit = _mesa_get_tex_unit_err(ctx, unit, "glBindTextureUnit");
+   if (!texUnit)
+      return;
+
+   /* Check if this texture is only used by this context and is already bound.
+    * If so, just return.
+    */
+   {
+      bool early_out;
+      mtx_lock(&ctx->Shared->Mutex);
+      early_out = ((ctx->Shared->RefCount == 1)
+                   && (texObj == texUnit->CurrentTex[texObj->TargetIndex]));
+      mtx_unlock(&ctx->Shared->Mutex);
+      if (early_out) {
+         return;
+      }
+   }
+
+   /* flush before changing binding */
+   FLUSH_VERTICES(ctx, _NEW_TEXTURE);
+
+   _mesa_reference_texobj(&texUnit->CurrentTex[texObj->TargetIndex],
+                          texObj);
+   assert(texUnit->CurrentTex[texObj->TargetIndex]);
+   ctx->Texture.NumCurrentTexUsed = MAX2(ctx->Texture.NumCurrentTexUsed,
+                                         unit + 1);
+   texUnit->_BoundTextures |= (1 << texObj->TargetIndex);
+
+
+   /* Pass BindTexture call to device driver */
+   if (ctx->Driver.BindTexture) {
+      ctx->Driver.BindTexture(ctx, unit, texObj->Target, texObj);
+   }
+}
+
+/**
+ * Bind a named texture to the specified texture unit.
+ *
+ * \param unit texture unit.
+ * \param texture texture name.
+ *
+ * \sa glBindTexture().
+ *
+ * If the named texture is 0, this will reset each target for the specified
+ * texture unit to its default texture.
+ * If the named texture is not 0 or a recognized texture name, this throws
+ * GL_INVALID_OPERATION.
+ */
+void GLAPIENTRY
+_mesa_BindTextureUnit(GLuint unit, GLuint texture)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   struct gl_texture_object *texObj;
+
+   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+      _mesa_debug(ctx, "glBindTextureUnit %s %d\n",
+                  _mesa_lookup_enum_by_nr(GL_TEXTURE0+unit), (GLint) texture);
+
+   /* Section 8.1 (Texture Objects) of the OpenGL 4.5 core profile spec
+    * (20141030) says:
+    *    "When texture is zero, each of the targets enumerated at the
+    *    beginning of this section is reset to its default texture for the
+    *    corresponding texture image unit."
+    */
+   if (texture == 0) {
+      unbind_textures_from_unit(ctx, unit);
+      return;
+   }
+
+   /* Get the non-default texture object */
+   texObj = _mesa_lookup_texture(ctx, texture);
+
+   /* Error checking */
+   if (!texObj) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+         "glBindTextureUnit(non-gen name)");
+      return;
+   }
+   if (texObj->Target == 0) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glBindTextureUnit(target)");
+      return;
+   }
+   assert(valid_texture_object(texObj));
+
+   _mesa_bind_texture_unit(ctx, unit, texObj);
+}
+
+
+void GLAPIENTRY
+_mesa_BindTextures(GLuint first, GLsizei count, const GLuint *textures)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   GLint i;
+
+   /* The ARB_multi_bind spec says:
+    *
+    *     "An INVALID_OPERATION error is generated if <first> + <count>
+    *      is greater than the number of texture image units supported
+    *      by the implementation."
+    */
+   if (first + count > ctx->Const.MaxCombinedTextureImageUnits) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glBindTextures(first=%u + count=%d > the value of "
+                  "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS=%u)",
+                  first, count, ctx->Const.MaxCombinedTextureImageUnits);
+      return;
+   }
+
+   /* Flush before changing bindings */
+   FLUSH_VERTICES(ctx, 0);
+
+   ctx->Texture.NumCurrentTexUsed = MAX2(ctx->Texture.NumCurrentTexUsed,
+                                         first + count);
+
+   if (textures) {
+      /* Note that the error semantics for multi-bind commands differ from
+       * those of other GL commands.
+       *
+       * The issues section in the ARB_multi_bind spec says:
+       *
+       *    "(11) Typically, OpenGL specifies that if an error is generated by
+       *          a command, that command has no effect.  This is somewhat
+       *          unfortunate for multi-bind commands, because it would require
+       *          a first pass to scan the entire list of bound objects for
+       *          errors and then a second pass to actually perform the
+       *          bindings.  Should we have different error semantics?
+       *
+       *       RESOLVED:  Yes.  In this specification, when the parameters for
+       *       one of the <count> binding points are invalid, that binding
+       *       point is not updated and an error will be generated.  However,
+       *       other binding points in the same command will be updated if
+       *       their parameters are valid and no other error occurs."
+       */
+
+      _mesa_begin_texture_lookups(ctx);
+
+      for (i = 0; i < count; i++) {
+         if (textures[i] != 0) {
+            struct gl_texture_unit *texUnit = &ctx->Texture.Unit[first + i];
+            struct gl_texture_object *current = texUnit->_Current;
+            struct gl_texture_object *texObj;
+
+            if (current && current->Name == textures[i])
+               texObj = current;
+            else
+               texObj = _mesa_lookup_texture_locked(ctx, textures[i]);
+
+            if (texObj && texObj->Target != 0) {
+               const gl_texture_index targetIndex = texObj->TargetIndex;
+
+               if (texUnit->CurrentTex[targetIndex] != texObj) {
+                  /* Do the actual binding.  The refcount on the previously
+                   * bound texture object will be decremented.  It will be
+                   * deleted if the count hits zero.
+                   */
+                  _mesa_reference_texobj(&texUnit->CurrentTex[targetIndex],
+                                         texObj);
+
+                  texUnit->_BoundTextures |= (1 << targetIndex);
+                  ctx->NewState |= _NEW_TEXTURE;
+
+                  /* Pass the BindTexture call to the device driver */
+                  if (ctx->Driver.BindTexture)
+                     ctx->Driver.BindTexture(ctx, first + i,
+                                             texObj->Target, texObj);
+               }
+            } else {
+               /* The ARB_multi_bind spec says:
+                *
+                *     "An INVALID_OPERATION error is generated if any value
+                *      in <textures> is not zero or the name of an existing
+                *      texture object (per binding)."
+                */
+               _mesa_error(ctx, GL_INVALID_OPERATION,
+                           "glBindTextures(textures[%d]=%u is not zero "
+                           "or the name of an existing texture object)",
+                           i, textures[i]);
+            }
+         } else {
+            unbind_textures_from_unit(ctx, first + i);
+         }
+      }
+
+      _mesa_end_texture_lookups(ctx);
+   } else {
+      /* Unbind all textures in the range <first> through <first>+<count>-1 */
+      for (i = 0; i < count; i++)
+         unbind_textures_from_unit(ctx, first + i);
+   }
 }
 
 
 /**
  * Set texture priorities.
- * 
+ *
  * \param n number of textures.
  * \param texName texture names.
  * \param priorities corresponding texture priorities.
- * 
+ *
  * \sa glPrioritizeTextures().
- * 
+ *
  * Looks up each texture in the hash, clamps the corresponding priority between
  * 0.0 and 1.0, and calls dd_function_table::PrioritizeTexture.
  */
@@ -1363,13 +1991,14 @@ _mesa_PrioritizeTextures( GLsizei n, const GLuint *texName,
 
 /**
  * See if textures are loaded in texture memory.
- * 
+ *
  * \param n number of textures to query.
  * \param texName array with the texture names.
  * \param residences array which will hold the residence status.
  *
- * \return GL_TRUE if all textures are resident and \p residences is left unchanged, 
- * 
+ * \return GL_TRUE if all textures are resident and
+ *                 residences is left unchanged,
+ *
  * Note: we assume all textures are always resident
  */
 GLboolean GLAPIENTRY
@@ -1405,7 +2034,7 @@ _mesa_AreTexturesResident(GLsizei n, const GLuint *texName,
          return GL_FALSE;
       }
    }
-   
+
    return allResident;
 }
 
@@ -1417,7 +2046,7 @@ _mesa_AreTexturesResident(GLsizei n, const GLuint *texName,
  *
  * \return GL_TRUE if texture name corresponds to a texture, or GL_FALSE
  * otherwise.
- * 
+ *
  * \sa glIsTexture().
  *
  * Calls _mesa_HashLookup().
@@ -1456,7 +2085,7 @@ _mesa_IsTexture( GLuint texture )
 void
 _mesa_lock_context_textures( struct gl_context *ctx )
 {
-   _glthread_LOCK_MUTEX(ctx->Shared->TexMutex);
+   mtx_lock(&ctx->Shared->TexMutex);
 
    if (ctx->Shared->TextureStateStamp != ctx->TextureStateTimestamp) {
       ctx->NewState |= _NEW_TEXTURE;
@@ -1469,9 +2098,10 @@ void
 _mesa_unlock_context_textures( struct gl_context *ctx )
 {
    assert(ctx->Shared->TextureStateStamp == ctx->TextureStateTimestamp);
-   _glthread_UNLOCK_MUTEX(ctx->Shared->TexMutex);
+   mtx_unlock(&ctx->Shared->TexMutex);
 }
 
+
 void GLAPIENTRY
 _mesa_InvalidateTexSubImage(GLuint texture, GLint level, GLint xoffset,
                             GLint yoffset, GLint zoffset, GLsizei width,
@@ -1618,6 +2248,7 @@ _mesa_InvalidateTexSubImage(GLuint texture, GLint level, GLint xoffset,
    return;
 }
 
+
 void GLAPIENTRY
 _mesa_InvalidateTexImage(GLuint texture, GLint level)
 {