aboutsummaryrefslogtreecommitdiff
path: root/mesalib/src/mesa/main/texobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'mesalib/src/mesa/main/texobj.c')
-rw-r--r--mesalib/src/mesa/main/texobj.c469
1 files changed, 439 insertions, 30 deletions
diff --git a/mesalib/src/mesa/main/texobj.c b/mesalib/src/mesa/main/texobj.c
index f0ff605fc..59090db4e 100644
--- a/mesalib/src/mesa/main/texobj.c
+++ b/mesalib/src/mesa/main/texobj.c
@@ -49,6 +49,54 @@
/** \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 +108,22 @@ _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)
@@ -82,6 +146,87 @@ _mesa_lookup_texture_locked(struct gl_context *ctx, GLuint id)
_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;
+ }
+}
+
/**
* Allocate and initialize a new texture object. But don't put it into the
@@ -311,6 +456,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;
}
@@ -405,6 +552,9 @@ _mesa_reference_texobj_(struct gl_texture_object **ptr,
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);
@@ -541,6 +691,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).
*/
@@ -710,25 +868,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)
@@ -736,7 +890,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 ||
@@ -747,6 +901,15 @@ _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
@@ -954,6 +1117,20 @@ _mesa_total_texture_memory(struct gl_context *ctx)
}
+/**
+ * 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)
@@ -1007,38 +1184,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.
+ * 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.
*/
-void GLAPIENTRY
-_mesa_GenTextures( GLsizei n, GLuint *textures )
+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;
}
@@ -1055,15 +1240,28 @@ _mesa_GenTextures( GLsizei n, GLuint *textures )
/* 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) {
mtx_unlock(&ctx->Shared->Mutex);
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTextures");
+ _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);
@@ -1073,6 +1271,65 @@ _mesa_GenTextures( GLsizei n, GLuint *textures )
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
@@ -1207,8 +1464,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;
@@ -1257,6 +1524,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
@@ -1428,6 +1736,107 @@ _mesa_BindTexture( GLenum target, GLuint texName )
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)