X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ftexobj.c;h=3cfbfa5eb549b65753f435850faf8fd092bdf1bd;hb=1c70cde8881f794782780cbd695da0882f78c769;hp=5a9b4a1c86c66f88ec464ebedc67d316d631fe54;hpb=9a4a958a5c45024abeb5ec97419362c05c8b76ac;p=mesa.git diff --git a/src/mesa/main/texobj.c b/src/mesa/main/texobj.c index 5a9b4a1c86c..3cfbfa5eb54 100644 --- a/src/mesa/main/texobj.c +++ b/src/mesa/main/texobj.c @@ -1,21 +1,24 @@ -/* $Id: texobj.c,v 1.28 2000/10/24 01:13:58 brianp Exp $ */ +/** + * \file texobj.c + * Texture object management. + */ /* * Mesa 3-D graphics library - * Version: 3.5 - * - * Copyright (C) 1999-2000 Brian Paul All Rights Reserved. - * + * Version: 6.5 + * + * Copyright (C) 1999-2006 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"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL @@ -25,243 +28,359 @@ */ -#ifdef PC_HEADER -#include "all.h" -#else #include "glheader.h" #include "colortab.h" #include "context.h" #include "enums.h" +#include "fbobject.h" #include "hash.h" -#include "mem.h" +#include "imports.h" +#include "macros.h" #include "teximage.h" #include "texstate.h" #include "texobj.h" -#include "types.h" +#include "mtypes.h" + + +#ifdef __VMS +#define _mesa_sprintf sprintf #endif +/**********************************************************************/ +/** \name Internal functions */ +/*@{*/ -/* - * Allocate a new texture object and add it to the linked list of texture - * objects. If name>0 then also insert the new texture object into the hash - * table. - * Input: shared - the shared GL state structure to contain the texture object - * name - integer name for the texture object - * dimensions - either 1, 2, 3 or 6 (cube map) - * Return: pointer to new texture object +/** + * Return the gl_texture_object for a given ID. */ struct gl_texture_object * -gl_alloc_texture_object( struct gl_shared_state *shared, GLuint name, - GLuint dimensions) +_mesa_lookup_texture(GLcontext *ctx, GLuint id) { - struct gl_texture_object *obj; + return (struct gl_texture_object *) + _mesa_HashLookup(ctx->Shared->TexObjects, id); +} - ASSERT(dimensions <= 3 || dimensions == 6); - obj = CALLOC_STRUCT(gl_texture_object); - if (obj) { - /* init the non-zero fields */ - _glthread_INIT_MUTEX(obj->Mutex); - obj->RefCount = 1; - obj->Name = name; - obj->Dimensions = dimensions; - obj->Priority = 1.0F; +/** + * Allocate and initialize a new texture object. But don't put it into the + * texture object hash table. + * + * 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, + * GL_TEXTURE_CUBE_MAP_ARB or GL_TEXTURE_RECTANGLE_NV. zero is ok for the sake + * of GenTextures() + * + * \return pointer to new texture object. + */ +struct gl_texture_object * +_mesa_new_texture_object( GLcontext *ctx, GLuint name, GLenum target ) +{ + struct gl_texture_object *obj; + (void) ctx; + obj = MALLOC_STRUCT(gl_texture_object); + _mesa_initialize_texture_object(obj, name, target); + return obj; +} + + +/** + * Initialize a new texture object to default values. + * \param obj the texture object + * \param name the texture name + * \param target the texture target + */ +void +_mesa_initialize_texture_object( struct gl_texture_object *obj, + GLuint name, GLenum target ) +{ + ASSERT(target == 0 || + target == GL_TEXTURE_1D || + target == GL_TEXTURE_2D || + target == GL_TEXTURE_3D || + target == GL_TEXTURE_CUBE_MAP_ARB || + target == GL_TEXTURE_RECTANGLE_NV); + + _mesa_bzero(obj, sizeof(*obj)); + /* init the non-zero fields */ + _glthread_INIT_MUTEX(obj->Mutex); + obj->RefCount = 1; + obj->Name = name; + obj->Target = target; + obj->Priority = 1.0F; + if (target == GL_TEXTURE_RECTANGLE_NV) { + obj->WrapS = GL_CLAMP_TO_EDGE; + obj->WrapT = GL_CLAMP_TO_EDGE; + obj->WrapR = GL_CLAMP_TO_EDGE; + obj->MinFilter = GL_LINEAR; + } + else { obj->WrapS = GL_REPEAT; obj->WrapT = GL_REPEAT; + obj->WrapR = GL_REPEAT; obj->MinFilter = GL_NEAREST_MIPMAP_LINEAR; - obj->MagFilter = GL_LINEAR; - obj->MinLod = -1000.0; - obj->MaxLod = 1000.0; - obj->BaseLevel = 0; - obj->MaxLevel = 1000; - obj->MinMagThresh = 0.0F; - _mesa_init_colortable(&obj->Palette); - - /* insert into linked list */ - if (shared) { - _glthread_LOCK_MUTEX(shared->Mutex); - obj->Next = shared->TexObjectList; - shared->TexObjectList = obj; - _glthread_UNLOCK_MUTEX(shared->Mutex); - } - - if (name > 0) { - /* insert into hash table */ - _mesa_HashInsert(shared->TexObjects, name, obj); - } } - return obj; + obj->MagFilter = GL_LINEAR; + obj->MinLod = -1000.0; + obj->MaxLod = 1000.0; + obj->LodBias = 0.0; + obj->BaseLevel = 0; + obj->MaxLevel = 1000; + obj->MaxAnisotropy = 1.0; + obj->CompareFlag = GL_FALSE; /* SGIX_shadow */ + obj->CompareOperator = GL_TEXTURE_LEQUAL_R_SGIX; /* SGIX_shadow */ + obj->CompareMode = GL_NONE; /* ARB_shadow */ + obj->CompareFunc = GL_LEQUAL; /* ARB_shadow */ + obj->DepthMode = GL_LUMINANCE; /* ARB_depth_texture */ + obj->ShadowAmbient = 0.0F; /* ARB/SGIX_shadow_ambient */ + _mesa_init_colortable(&obj->Palette); } -/* - * Deallocate a texture object struct and remove it from the given - * shared GL state. - * Input: shared - the shared GL state to which the object belongs - * t - the texture object to delete +/** + * Deallocate a texture object struct. It should have already been + * removed from the texture object pool. + * + * \param shared the shared GL state to which the object belongs. + * \param texOjb the texture object to delete. */ -void gl_free_texture_object( struct gl_shared_state *shared, - struct gl_texture_object *t ) +void +_mesa_delete_texture_object( GLcontext *ctx, struct gl_texture_object *texObj ) { - struct gl_texture_object *tprev, *tcurr; + GLuint i, face; - assert(t); + (void) ctx; - /* Remove t from dirty list so we don't touch free'd memory later. - * Test for shared since Proxy texture aren't in global linked list. - */ - if (shared) - gl_remove_texobj_from_dirty_list( shared, t ); - - /* unlink t from the linked list */ - if (shared) { - _glthread_LOCK_MUTEX(shared->Mutex); - tprev = NULL; - tcurr = shared->TexObjectList; - while (tcurr) { - if (tcurr==t) { - if (tprev) { - tprev->Next = t->Next; - } - else { - shared->TexObjectList = t->Next; - } - break; - } - tprev = tcurr; - tcurr = tcurr->Next; - } - _glthread_UNLOCK_MUTEX(shared->Mutex); - } + _mesa_free_colortable_data(&texObj->Palette); - if (t->Name) { - /* remove from hash table */ - _mesa_HashRemove(shared->TexObjects, t->Name); + /* free the texture images */ + for (face = 0; face < 6; face++) { + for (i = 0; i < MAX_TEXTURE_LEVELS; i++) { + if (texObj->Image[face][i]) { + _mesa_delete_texture_image( ctx, texObj->Image[face][i] ); + } + } } - _mesa_free_colortable_data(&t->Palette); + /* destroy the mutex -- it may have allocated memory (eg on bsd) */ + _glthread_DESTROY_MUTEX(texObj->Mutex); - /* free texture images */ - { - GLuint i; - for (i=0;iImage[i]) { - _mesa_free_texture_image( t->Image[i] ); - } - } - } /* free this object */ - FREE( t ); + _mesa_free(texObj); } -/* - * Examine a texture object to determine if it is complete or not. - * The t->Complete flag will be set to GL_TRUE or GL_FALSE accordingly. + +/** + * Copy texture object state from one texture object to another. + * Use for glPush/PopAttrib. + * + * \param dest destination texture object. + * \param src source texture object. + */ +void +_mesa_copy_texture_object( struct gl_texture_object *dest, + const struct gl_texture_object *src ) +{ + dest->Name = src->Name; + dest->Priority = src->Priority; + dest->BorderColor[0] = src->BorderColor[0]; + dest->BorderColor[1] = src->BorderColor[1]; + dest->BorderColor[2] = src->BorderColor[2]; + dest->BorderColor[3] = src->BorderColor[3]; + dest->WrapS = src->WrapS; + dest->WrapT = src->WrapT; + dest->WrapR = src->WrapR; + dest->MinFilter = src->MinFilter; + dest->MagFilter = src->MagFilter; + dest->MinLod = src->MinLod; + dest->MaxLod = src->MaxLod; + dest->LodBias = src->LodBias; + dest->BaseLevel = src->BaseLevel; + dest->MaxLevel = src->MaxLevel; + dest->MaxAnisotropy = src->MaxAnisotropy; + dest->CompareFlag = src->CompareFlag; + dest->CompareOperator = src->CompareOperator; + dest->ShadowAmbient = src->ShadowAmbient; + dest->CompareMode = src->CompareMode; + dest->CompareFunc = src->CompareFunc; + dest->DepthMode = src->DepthMode; + dest->_MaxLevel = src->_MaxLevel; + dest->_MaxLambda = src->_MaxLambda; + dest->GenerateMipmap = src->GenerateMipmap; + dest->Palette = src->Palette; + dest->Complete = src->Complete; +} + + +/** + * Report why a texture object is incomplete. + * + * \param t texture object. + * \param why string describing why it's incomplete. + * + * \note For debug purposes only. + */ +#if 0 +static void +incomplete(const struct gl_texture_object *t, const char *why) +{ + _mesa_printf("Texture Obj %d incomplete because: %s\n", t->Name, why); +} +#else +#define incomplete(t, why) +#endif + + +/** + * Examine a texture object to determine if it is complete. + * + * The gl_texture_object::Complete flag will be set to GL_TRUE or GL_FALSE + * accordingly. + * + * \param ctx GL context. + * \param t texture object. + * + * According to the texture target, verifies that each of the mipmaps is + * present and has the expected size. */ void _mesa_test_texobj_completeness( const GLcontext *ctx, struct gl_texture_object *t ) { const GLint baseLevel = t->BaseLevel; + GLint maxLog2 = 0, maxLevels = 0; t->Complete = GL_TRUE; /* be optimistic */ - /* Always need level zero image */ - if (!t->Image[baseLevel]) { + /* Always need the base level image */ + if (!t->Image[0][baseLevel]) { + char s[100]; + _mesa_sprintf(s, "obj %p (%d) Image[baseLevel=%d] == NULL", + (void *) t, t->Name, baseLevel); + incomplete(t, s); t->Complete = GL_FALSE; return; } - /* Compute number of mipmap levels */ - if (t->Dimensions == 1) { - t->P = t->Image[baseLevel]->WidthLog2; + /* Check width/height/depth for zero */ + if (t->Image[0][baseLevel]->Width == 0 || + t->Image[0][baseLevel]->Height == 0 || + t->Image[0][baseLevel]->Depth == 0) { + incomplete(t, "texture width = 0"); + t->Complete = GL_FALSE; + return; + } + + /* Compute _MaxLevel */ + if (t->Target == GL_TEXTURE_1D) { + maxLog2 = t->Image[0][baseLevel]->WidthLog2; + maxLevels = ctx->Const.MaxTextureLevels; + } + else if (t->Target == GL_TEXTURE_2D) { + maxLog2 = MAX2(t->Image[0][baseLevel]->WidthLog2, + t->Image[0][baseLevel]->HeightLog2); + maxLevels = ctx->Const.MaxTextureLevels; } - else if (t->Dimensions == 2 || t->Dimensions == 6) { - t->P = MAX2(t->Image[baseLevel]->WidthLog2, - t->Image[baseLevel]->HeightLog2); + else if (t->Target == GL_TEXTURE_3D) { + GLint max = MAX2(t->Image[0][baseLevel]->WidthLog2, + t->Image[0][baseLevel]->HeightLog2); + maxLog2 = MAX2(max, (GLint)(t->Image[0][baseLevel]->DepthLog2)); + maxLevels = ctx->Const.Max3DTextureLevels; } - else if (t->Dimensions == 3) { - GLint max = MAX2(t->Image[baseLevel]->WidthLog2, - t->Image[baseLevel]->HeightLog2); - max = MAX2(max, (GLint)(t->Image[baseLevel]->DepthLog2)); - t->P = max; + else if (t->Target == GL_TEXTURE_CUBE_MAP_ARB) { + maxLog2 = MAX2(t->Image[0][baseLevel]->WidthLog2, + t->Image[0][baseLevel]->HeightLog2); + maxLevels = ctx->Const.MaxCubeTextureLevels; + } + else if (t->Target == GL_TEXTURE_RECTANGLE_NV) { + maxLog2 = 0; /* not applicable */ + maxLevels = 1; /* no mipmapping */ + } + else { + _mesa_problem(ctx, "Bad t->Target in _mesa_test_texobj_completeness"); + return; } - /* Compute M (see the 1.2 spec) used during mipmapping */ - t->M = (GLfloat) (MIN2(t->MaxLevel, t->P) - t->BaseLevel); - - - if (t->Dimensions == 6) { - /* make sure all six level 0 images are same size */ - const GLint w = t->Image[baseLevel]->Width2; - const GLint h = t->Image[baseLevel]->Height2; - if (!t->NegX[baseLevel] || - t->NegX[baseLevel]->Width2 != w || - t->NegX[baseLevel]->Height2 != h || - !t->PosY[baseLevel] || - t->PosY[baseLevel]->Width2 != w || - t->PosY[baseLevel]->Height2 != h || - !t->NegY[baseLevel] || - t->NegY[baseLevel]->Width2 != w || - t->NegY[baseLevel]->Height2 != h || - !t->PosZ[baseLevel] || - t->PosZ[baseLevel]->Width2 != w || - t->PosZ[baseLevel]->Height2 != h || - !t->NegZ[baseLevel] || - t->NegZ[baseLevel]->Width2 != w || - t->NegZ[baseLevel]->Height2 != h) { - t->Complete = GL_FALSE; - return; + ASSERT(maxLevels > 0); + + t->_MaxLevel = baseLevel + maxLog2; + t->_MaxLevel = MIN2(t->_MaxLevel, t->MaxLevel); + t->_MaxLevel = MIN2(t->_MaxLevel, maxLevels - 1); + + /* Compute _MaxLambda = q - b (see the 1.2 spec) used during mipmapping */ + t->_MaxLambda = (GLfloat) (t->_MaxLevel - t->BaseLevel); + + if (t->Target == GL_TEXTURE_CUBE_MAP_ARB) { + /* make sure that all six cube map level 0 images are the same size */ + const GLuint w = t->Image[0][baseLevel]->Width2; + const GLuint h = t->Image[0][baseLevel]->Height2; + GLuint face; + for (face = 1; face < 6; face++) { + if (t->Image[face][baseLevel] == NULL || + t->Image[face][baseLevel]->Width2 != w || + t->Image[face][baseLevel]->Height2 != h) { + t->Complete = GL_FALSE; + incomplete(t, "Non-quare cubemap image"); + return; + } } } + /* extra checking for mipmaps */ if (t->MinFilter != GL_NEAREST && t->MinFilter != GL_LINEAR) { /* * Mipmapping: determine if we have a complete set of mipmaps */ GLint i; GLint minLevel = baseLevel; - GLint maxLevel = MIN2(t->P, ctx->Const.MaxTextureLevels-1); - maxLevel = MIN2(maxLevel, t->MaxLevel); + GLint maxLevel = t->_MaxLevel; if (minLevel > maxLevel) { t->Complete = GL_FALSE; + incomplete(t, "minLevel > maxLevel"); return; } /* Test dimension-independent attributes */ for (i = minLevel; i <= maxLevel; i++) { - if (t->Image[i]) { - if (t->Image[i]->Format != t->Image[baseLevel]->Format) { + if (t->Image[0][i]) { + if (t->Image[0][i]->TexFormat != t->Image[0][baseLevel]->TexFormat) { t->Complete = GL_FALSE; + incomplete(t, "Format[i] != Format[baseLevel]"); return; } - if (t->Image[i]->Border != t->Image[baseLevel]->Border) { + if (t->Image[0][i]->Border != t->Image[0][baseLevel]->Border) { t->Complete = GL_FALSE; + incomplete(t, "Border[i] != Border[baseLevel]"); return; } } } /* Test things which depend on number of texture image dimensions */ - if (t->Dimensions == 1) { + if (t->Target == GL_TEXTURE_1D) { /* Test 1-D mipmaps */ - GLuint width = t->Image[baseLevel]->Width2; - for (i = baseLevel + 1; i < ctx->Const.MaxTextureLevels; i++) { + GLuint width = t->Image[0][baseLevel]->Width2; + for (i = baseLevel + 1; i < maxLevels; i++) { if (width > 1) { width /= 2; } if (i >= minLevel && i <= maxLevel) { - if (!t->Image[i]) { + if (!t->Image[0][i]) { t->Complete = GL_FALSE; + incomplete(t, "1D Image[0][i] == NULL"); return; } - if (t->Image[i]->Width2 != width ) { + if (t->Image[0][i]->Width2 != width ) { t->Complete = GL_FALSE; + incomplete(t, "1D Image[0][i] bad width"); return; } } @@ -270,11 +389,11 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, } } } - else if (t->Dimensions == 2) { + else if (t->Target == GL_TEXTURE_2D) { /* Test 2-D mipmaps */ - GLuint width = t->Image[baseLevel]->Width2; - GLuint height = t->Image[baseLevel]->Height2; - for (i = baseLevel + 1; i < ctx->Const.MaxTextureLevels; i++) { + GLuint width = t->Image[0][baseLevel]->Width2; + GLuint height = t->Image[0][baseLevel]->Height2; + for (i = baseLevel + 1; i < maxLevels; i++) { if (width > 1) { width /= 2; } @@ -282,16 +401,19 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, height /= 2; } if (i >= minLevel && i <= maxLevel) { - if (!t->Image[i]) { + if (!t->Image[0][i]) { t->Complete = GL_FALSE; + incomplete(t, "2D Image[0][i] == NULL"); return; } - if (t->Image[i]->Width2 != width) { + if (t->Image[0][i]->Width2 != width) { t->Complete = GL_FALSE; + incomplete(t, "2D Image[0][i] bad width"); return; } - if (t->Image[i]->Height2 != height) { + if (t->Image[0][i]->Height2 != height) { t->Complete = GL_FALSE; + incomplete(t, "2D Image[0][i] bad height"); return; } if (width==1 && height==1) { @@ -300,12 +422,12 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, } } } - else if (t->Dimensions == 3) { + else if (t->Target == GL_TEXTURE_3D) { /* Test 3-D mipmaps */ - GLuint width = t->Image[baseLevel]->Width2; - GLuint height = t->Image[baseLevel]->Height2; - GLuint depth = t->Image[baseLevel]->Depth2; - for (i = baseLevel + 1; i < ctx->Const.MaxTextureLevels; i++) { + GLuint width = t->Image[0][baseLevel]->Width2; + GLuint height = t->Image[0][baseLevel]->Height2; + GLuint depth = t->Image[0][baseLevel]->Depth2; + for (i = baseLevel + 1; i < maxLevels; i++) { if (width > 1) { width /= 2; } @@ -316,20 +438,29 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, depth /= 2; } if (i >= minLevel && i <= maxLevel) { - if (!t->Image[i]) { + if (!t->Image[0][i]) { + incomplete(t, "3D Image[0][i] == NULL"); + t->Complete = GL_FALSE; + return; + } + if (t->Image[0][i]->_BaseFormat == GL_DEPTH_COMPONENT) { t->Complete = GL_FALSE; + incomplete(t, "GL_DEPTH_COMPONENT only works with 1/2D tex"); return; } - if (t->Image[i]->Width2 != width) { + if (t->Image[0][i]->Width2 != width) { t->Complete = GL_FALSE; + incomplete(t, "3D Image[0][i] bad width"); return; } - if (t->Image[i]->Height2 != height) { + if (t->Image[0][i]->Height2 != height) { t->Complete = GL_FALSE; + incomplete(t, "3D Image[0][i] bad height"); return; } - if (t->Image[i]->Depth2 != depth) { + if (t->Image[0][i]->Depth2 != depth) { t->Complete = GL_FALSE; + incomplete(t, "3D Image[0][i] bad depth"); return; } } @@ -338,11 +469,11 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, } } } - else if (t->Dimensions == 6) { + else if (t->Target == GL_TEXTURE_CUBE_MAP_ARB) { /* make sure 6 cube faces are consistant */ - GLuint width = t->Image[baseLevel]->Width2; - GLuint height = t->Image[baseLevel]->Height2; - for (i = baseLevel + 1; i < ctx->Const.MaxTextureLevels; i++) { + GLuint width = t->Image[0][baseLevel]->Width2; + GLuint height = t->Image[0][baseLevel]->Height2; + for (i = baseLevel + 1; i < maxLevels; i++) { if (width > 1) { width /= 2; } @@ -350,56 +481,85 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, height /= 2; } if (i >= minLevel && i <= maxLevel) { - /* check that we have images defined */ - if (!t->Image[i] || !t->NegX[i] || - !t->PosY[i] || !t->NegY[i] || - !t->PosZ[i] || !t->NegZ[i]) { - t->Complete = GL_FALSE; - return; - } - /* check that all six images have same size */ - if (t->NegX[i]->Width2!=width || t->NegX[i]->Height2!=height || - t->PosY[i]->Width2!=width || t->PosY[i]->Height2!=height || - t->NegY[i]->Width2!=width || t->NegY[i]->Height2!=height || - t->PosZ[i]->Width2!=width || t->PosZ[i]->Height2!=height || - t->NegZ[i]->Width2!=width || t->NegZ[i]->Height2!=height) { - t->Complete = GL_FALSE; - return; - } - } - if (width == 1 && height == 1) { - return; /* found smallest needed mipmap, all done! */ + GLuint face; + for (face = 0; face < 6; face++) { + /* check that we have images defined */ + if (!t->Image[face][i]) { + t->Complete = GL_FALSE; + incomplete(t, "CubeMap Image[n][i] == NULL"); + return; + } + /* Don't support GL_DEPTH_COMPONENT for cube maps */ + if (t->Image[face][i]->_BaseFormat == GL_DEPTH_COMPONENT) { + t->Complete = GL_FALSE; + incomplete(t, "GL_DEPTH_COMPONENT only works with 1/2D tex"); + return; + } + /* check that all six images have same size */ + if (t->Image[face][i]->Width2!=width || + t->Image[face][i]->Height2!=height) { + t->Complete = GL_FALSE; + incomplete(t, "CubeMap Image[n][i] bad size"); + return; + } + } + } + if (width == 1 && height == 1) { + return; /* found smallest needed mipmap, all done! */ } } } + else if (t->Target == GL_TEXTURE_RECTANGLE_NV) { + /* XXX special checking? */ + } else { - /* Dimensions = ??? */ - gl_problem(NULL, "Bug in gl_test_texture_object_completeness\n"); + /* Target = ??? */ + _mesa_problem(ctx, "Bug in gl_test_texture_object_completeness\n"); } } } +/*@}*/ -_glthread_DECLARE_STATIC_MUTEX(GenTexturesLock); +/***********************************************************************/ +/** \name API functions */ +/*@{*/ -/* - * Execute glGenTextures +/** + * Texture name generation lock. + * + * Used by _mesa_GenTextures() to guarantee that the generation and allocation + * of texture IDs is atomic. */ -void -_mesa_GenTextures( GLsizei n, GLuint *texName ) +_glthread_DECLARE_STATIC_MUTEX(GenTexturesLock); + +/** + * 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(). + * + * While holding the GenTexturesLock lock, 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); GLuint first; GLint i; + ASSERT_OUTSIDE_BEGIN_END(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glGenTextures"); if (n < 0) { - gl_error( ctx, GL_INVALID_VALUE, "glGenTextures" ); + _mesa_error( ctx, GL_INVALID_VALUE, "glGenTextures" ); return; } - if (!texName) + if (!textures) return; /* @@ -409,67 +569,176 @@ _mesa_GenTextures( GLsizei n, GLuint *texName ) first = _mesa_HashFindFreeKeyBlock(ctx->Shared->TexObjects, n); - /* Return the texture names */ - for (i=0;iShared, name, dims); + GLenum target = 0; + texObj = (*ctx->Driver.NewTextureObject)( ctx, name, target); + if (!texObj) { + _glthread_UNLOCK_MUTEX(GenTexturesLock); + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTextures"); + return; + } + + /* insert into hash table */ + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); + _mesa_HashInsert(ctx->Shared->TexObjects, texObj->Name, texObj); + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); + + textures[i] = name; } _glthread_UNLOCK_MUTEX(GenTexturesLock); } +/** + * Check if the given texture object is bound to the current draw or + * read framebuffer. If so, Unbind it. + */ +static void +unbind_texobj_from_fbo(GLcontext *ctx, struct gl_texture_object *texObj) +{ + const GLuint n = (ctx->DrawBuffer == ctx->ReadBuffer) ? 1 : 2; + GLuint i; -/* - * Execute glDeleteTextures + for (i = 0; i < n; i++) { + struct gl_framebuffer *fb = (i == 0) ? ctx->DrawBuffer : ctx->ReadBuffer; + if (fb->Name) { + GLuint j; + for (j = 0; j < BUFFER_COUNT; j++) { + if (fb->Attachment[j].Type == GL_TEXTURE && + fb->Attachment[j].Texture == texObj) { + _mesa_remove_attachment(ctx, fb->Attachment + j); + } + } + } + } +} + + +/** + * 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. */ -void -_mesa_DeleteTextures( GLsizei n, const GLuint *texName) +static void +unbind_texobj_from_texunits(GLcontext *ctx, struct gl_texture_object *texObj) +{ + GLuint u; + + for (u = 0; u < MAX_TEXTURE_IMAGE_UNITS; u++) { + struct gl_texture_unit *unit = &ctx->Texture.Unit[u]; + if (texObj == unit->Current1D) { + unit->Current1D = ctx->Shared->Default1D; + ctx->Shared->Default1D->RefCount++; + texObj->RefCount--; + if (texObj == unit->_Current) + unit->_Current = unit->Current1D; + } + else if (texObj == unit->Current2D) { + unit->Current2D = ctx->Shared->Default2D; + ctx->Shared->Default2D->RefCount++; + texObj->RefCount--; + if (texObj == unit->_Current) + unit->_Current = unit->Current2D; + } + else if (texObj == unit->Current3D) { + unit->Current3D = ctx->Shared->Default3D; + ctx->Shared->Default3D->RefCount++; + texObj->RefCount--; + if (texObj == unit->_Current) + unit->_Current = unit->Current3D; + } + else if (texObj == unit->CurrentCubeMap) { + unit->CurrentCubeMap = ctx->Shared->DefaultCubeMap; + ctx->Shared->DefaultCubeMap->RefCount++; + texObj->RefCount--; + if (texObj == unit->_Current) + unit->_Current = unit->CurrentCubeMap; + } + else if (texObj == unit->CurrentRect) { + unit->CurrentRect = ctx->Shared->DefaultRect; + ctx->Shared->DefaultRect->RefCount++; + texObj->RefCount--; + if (texObj == unit->_Current) + unit->_Current = unit->CurrentRect; + } + } +} + + +/** + * Delete named textures. + * + * \param n number of textures to be deleted. + * \param textures array of texture IDs to be deleted. + * + * \sa glDeleteTextures(). + * + * If we're about to delete a texture that's currently bound to any + * texture unit, unbind the texture first. Decrement the reference + * count on the texture object and delete it if it's zero. + * Recall that texture objects can be shared among several rendering + * contexts. + */ +void GLAPIENTRY +_mesa_DeleteTextures( GLsizei n, const GLuint *textures) { GET_CURRENT_CONTEXT(ctx); GLint i; + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); /* too complex */ - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glDeleteTextures"); - - if (!texName) + if (!textures) return; - for (i=0;i0) { - t = (struct gl_texture_object *) - _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]); - if (t) { - /* First check if this texture is currently bound. + for (i = 0; i < n; i++) { + if (textures[i] > 0) { + struct gl_texture_object *delObj + = _mesa_lookup_texture(ctx, textures[i]); + + if (delObj) { + GLboolean deleted; + + _mesa_lock_texture(ctx, delObj); + + /* 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, delObj); + + /* Check if this texture is currently bound to any texture units. * If so, unbind it and decrement the reference count. */ - GLuint u; - for (u = 0; u < MAX_TEXTURE_UNITS; u++) { - struct gl_texture_unit *unit = &ctx->Texture.Unit[u]; - GLuint d; - for (d = 1 ; d <= 3 ; d++) { - if (unit->CurrentD[d] == t) { - unit->CurrentD[d] = ctx->Shared->DefaultD[d]; - ctx->Shared->DefaultD[d]->RefCount++; - t->RefCount--; - ASSERT( t->RefCount >= 0 ); - } - } - } + unbind_texobj_from_texunits(ctx, delObj); + + ctx->NewState |= _NEW_TEXTURE; + + /* The texture _name_ is now free for re-use. + * Remove it from the hash table now. + */ + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); + _mesa_HashRemove(ctx->Shared->TexObjects, delObj->Name); + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); - /* Decrement reference count and delete if zero */ - t->RefCount--; - ASSERT( t->RefCount >= 0 ); - if (t->RefCount == 0) { - if (ctx->Driver.DeleteTexture) - (*ctx->Driver.DeleteTexture)( ctx, t ); - gl_free_texture_object(ctx->Shared, t); + /* 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. + */ + 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); } } } @@ -477,151 +746,213 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *texName) } - -/* - * Execute glBindTexture +/** + * 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 + * the same texture. Get the current texture which is either a default texture + * if name is null, a named texture from the hash, or a new texture if the + * given texture name is new. Increments its reference count, binds it, and + * calls dd_function_table::BindTexture. Decrements the old texture reference + * count and deletes it if it reaches zero. */ -void +void GLAPIENTRY _mesa_BindTexture( GLenum target, GLuint texName ) { GET_CURRENT_CONTEXT(ctx); - GLuint unit = ctx->Texture.CurrentUnit; + 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; - GLuint dim; + struct gl_texture_object *newTexObj = NULL; + ASSERT_OUTSIDE_BEGIN_END(ctx); if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE)) - fprintf(stderr, "glBindTexture %s %d\n", - gl_lookup_enum_by_nr(target), (GLint) texName); - - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glBindTexture"); + _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: - dim = 1; - oldTexObj = texUnit->CurrentD[1]; + oldTexObj = texUnit->Current1D; break; case GL_TEXTURE_2D: - dim = 2; - oldTexObj = texUnit->CurrentD[2]; + oldTexObj = texUnit->Current2D; break; case GL_TEXTURE_3D: - dim = 3; - oldTexObj = texUnit->CurrentD[3]; + oldTexObj = texUnit->Current3D; break; case GL_TEXTURE_CUBE_MAP_ARB: - if (ctx->Extensions.HaveTextureCubeMap) { - dim = 6; - oldTexObj = texUnit->CurrentCubeMap; - break; + if (!ctx->Extensions.ARB_texture_cube_map) { + _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" ); + return; } - /* fallthrough */ + 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; default: - gl_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" ); + _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" ); return; } - if (oldTexObj->Name == texName) - 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) + */ if (texName == 0) { - if (target == GL_TEXTURE_CUBE_MAP_ARB) - newTexObj = ctx->Shared->DefaultCubeMap; - else - newTexObj = ctx->Shared->DefaultD[dim]; + /* newTexObj = a default texture object */ + switch (target) { + case GL_TEXTURE_1D: + newTexObj = ctx->Shared->Default1D; + break; + case GL_TEXTURE_2D: + newTexObj = ctx->Shared->Default2D; + break; + case GL_TEXTURE_3D: + newTexObj = ctx->Shared->Default3D; + break; + case GL_TEXTURE_CUBE_MAP_ARB: + newTexObj = ctx->Shared->DefaultCubeMap; + break; + case GL_TEXTURE_RECTANGLE_NV: + newTexObj = ctx->Shared->DefaultRect; + break; + default: + ; /* Bad targets are caught above */ + } } else { - struct _mesa_HashTable *hash = ctx->Shared->TexObjects; - newTexObj = (struct gl_texture_object *) _mesa_HashLookup(hash, texName); - - if (!newTexObj) - newTexObj = gl_alloc_texture_object(ctx->Shared, texName, dim); - - if (newTexObj->Dimensions != dim) { - if (newTexObj->Dimensions) { + /* non-default texture object */ + newTexObj = _mesa_lookup_texture(ctx, texName); + if (newTexObj) { + /* error checking */ + if (newTexObj->Target != 0 && newTexObj->Target != target) { /* the named texture object's dimensions don't match the target */ - gl_error( ctx, GL_INVALID_OPERATION, "glBindTexture" ); - return; - } - newTexObj->Dimensions = dim; + _mesa_error( ctx, GL_INVALID_OPERATION, + "glBindTexture(wrong dimensionality)" ); + return; + } + if (newTexObj->Target == 0 && target == GL_TEXTURE_RECTANGLE_NV) { + /* have to init wrap and filter state here - kind of klunky */ + newTexObj->WrapS = GL_CLAMP_TO_EDGE; + newTexObj->WrapT = GL_CLAMP_TO_EDGE; + newTexObj->WrapR = GL_CLAMP_TO_EDGE; + newTexObj->MinFilter = GL_LINEAR; + if (ctx->Driver.TexParameter) { + static const GLfloat fparam_wrap[1] = {(GLfloat) GL_CLAMP_TO_EDGE}; + static const GLfloat fparam_filter[1] = {(GLfloat) GL_LINEAR}; + (*ctx->Driver.TexParameter)( ctx, target, newTexObj, GL_TEXTURE_WRAP_S, fparam_wrap ); + (*ctx->Driver.TexParameter)( ctx, target, newTexObj, GL_TEXTURE_WRAP_T, fparam_wrap ); + (*ctx->Driver.TexParameter)( ctx, target, newTexObj, GL_TEXTURE_WRAP_R, fparam_wrap ); + (*ctx->Driver.TexParameter)( ctx, target, newTexObj, GL_TEXTURE_MIN_FILTER, fparam_filter ); + } + } } + else { + /* if this is a new texture id, allocate a texture object now */ + newTexObj = (*ctx->Driver.NewTextureObject)(ctx, texName, target); + if (!newTexObj) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindTexture"); + return; + } + + /* and insert it into hash table */ + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); + _mesa_HashInsert(ctx->Shared->TexObjects, texName, newTexObj); + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); + } + newTexObj->Target = target; } + /* XXX all RefCount accesses should be protected by a mutex. */ newTexObj->RefCount++; + /* do the actual binding, but first flush outstanding vertices: + */ + FLUSH_VERTICES(ctx, _NEW_TEXTURE); + switch (target) { case GL_TEXTURE_1D: - texUnit->CurrentD[1] = newTexObj; + texUnit->Current1D = newTexObj; break; case GL_TEXTURE_2D: - texUnit->CurrentD[2] = newTexObj; + texUnit->Current2D = newTexObj; break; case GL_TEXTURE_3D: - texUnit->CurrentD[3] = newTexObj; + texUnit->Current3D = newTexObj; break; case GL_TEXTURE_CUBE_MAP_ARB: texUnit->CurrentCubeMap = newTexObj; break; + case GL_TEXTURE_RECTANGLE_NV: + texUnit->CurrentRect = newTexObj; + break; default: - gl_problem(ctx, "bad target in BindTexture"); - } - - /* If we've changed the CurrentD[123] texture object then update the - * ctx->Texture.Current pointer to point to the new texture object. - */ - texUnit->Current = texUnit->CurrentD[texUnit->CurrentDimension]; - - /* Check if we may have to use a new triangle rasterizer */ - if ((ctx->IndirectTriangles & DD_SW_RASTERIZE) && - ( oldTexObj->WrapS != newTexObj->WrapS - || oldTexObj->WrapT != newTexObj->WrapT - || oldTexObj->WrapR != newTexObj->WrapR - || oldTexObj->MinFilter != newTexObj->MinFilter - || oldTexObj->MagFilter != newTexObj->MagFilter - || (oldTexObj->Image[0] && newTexObj->Image[0] && - (oldTexObj->Image[0]->Format!=newTexObj->Image[0]->Format)))) - { - ctx->NewState |= (NEW_RASTER_OPS | NEW_TEXTURING); + _mesa_problem(ctx, "bad target in BindTexture"); + return; } - if (oldTexObj->Complete != newTexObj->Complete) - ctx->NewState |= NEW_TEXTURING; - /* Pass BindTexture call to device driver */ - if (ctx->Driver.BindTexture) { + if (ctx->Driver.BindTexture) (*ctx->Driver.BindTexture)( ctx, target, newTexObj ); - /* Make sure the Driver.UpdateState() function gets called! */ - ctx->NewState |= NEW_TEXTURING; - } - if (oldTexObj->Name > 0) { - /* never delete default (id=0) texture objects */ - oldTexObj->RefCount--; - if (oldTexObj->RefCount <= 0) { - if (ctx->Driver.DeleteTexture) { - (*ctx->Driver.DeleteTexture)( ctx, oldTexObj ); - } - gl_free_texture_object(ctx->Shared, oldTexObj); - } + /* 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 ); } } - -/* - * Execute glPrioritizeTextures +/** + * 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. */ -void +void GLAPIENTRY _mesa_PrioritizeTextures( GLsizei n, const GLuint *texName, const GLclampf *priorities ) { GET_CURRENT_CONTEXT(ctx); GLint i; + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPrioritizeTextures"); if (n < 0) { - gl_error( ctx, GL_INVALID_VALUE, "glPrioritizeTextures" ); + _mesa_error( ctx, GL_INVALID_VALUE, "glPrioritizeTextures" ); return; } @@ -630,8 +961,7 @@ _mesa_PrioritizeTextures( GLsizei n, const GLuint *texName, for (i = 0; i < n; i++) { if (texName[i] > 0) { - struct gl_texture_object *t = (struct gl_texture_object *) - _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]); + struct gl_texture_object *t = _mesa_lookup_texture(ctx, texName[i]); if (t) { t->Priority = CLAMP( priorities[i], 0.0F, 1.0F ); if (ctx->Driver.PrioritizeTexture) @@ -639,25 +969,35 @@ _mesa_PrioritizeTextures( GLsizei n, const GLuint *texName, } } } -} - + ctx->NewState |= _NEW_TEXTURE; +} -/* - * Execute glAreTexturesResident +/** + * 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, + * + * \sa glAreTexturesResident(). + * + * Looks up each texture in the hash and calls + * dd_function_table::IsTextureResident. */ -GLboolean +GLboolean GLAPIENTRY _mesa_AreTexturesResident(GLsizei n, const GLuint *texName, GLboolean *residences) { GET_CURRENT_CONTEXT(ctx); GLboolean allResident = GL_TRUE; - GLint i; + GLint i, j; + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, - "glAreTexturesResident", GL_FALSE); if (n < 0) { - gl_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)"); + _mesa_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)"); return GL_FALSE; } @@ -667,45 +1007,86 @@ _mesa_AreTexturesResident(GLsizei n, const GLuint *texName, for (i = 0; i < n; i++) { struct gl_texture_object *t; if (texName[i] == 0) { - gl_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)"); + _mesa_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident"); return GL_FALSE; } - t = (struct gl_texture_object *) - _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]); - if (t) { - if (ctx->Driver.IsTextureResident) { - residences[i] = ctx->Driver.IsTextureResident(ctx, t); - if (!residences[i]) - allResident = GL_FALSE; - } - else { + t = _mesa_lookup_texture(ctx, texName[i]); + if (!t) { + _mesa_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident"); + return GL_FALSE; + } + if (!ctx->Driver.IsTextureResident || + ctx->Driver.IsTextureResident(ctx, t)) { + /* The texture is resident */ + if (!allResident) residences[i] = GL_TRUE; - } } else { - gl_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident(textures)"); - return GL_FALSE; + /* The texture is not resident */ + if (allResident) { + allResident = GL_FALSE; + for (j = 0; j < i; j++) + residences[j] = GL_TRUE; + } + residences[i] = GL_FALSE; } } + return allResident; } - - -/* - * Execute glIsTexture +/** + * See if a name corresponds to a texture. + * + * \param texture texture name. + * + * \return GL_TRUE if texture name corresponds to a texture, or GL_FALSE + * otherwise. + * + * \sa glIsTexture(). + * + * Calls _mesa_HashLookup(). */ -GLboolean +GLboolean GLAPIENTRY _mesa_IsTexture( GLuint texture ) { + struct gl_texture_object *t; GET_CURRENT_CONTEXT(ctx); - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, "glIsTextures", - GL_FALSE); - if (texture > 0 && _mesa_HashLookup(ctx->Shared->TexObjects, texture)) { - return GL_TRUE; - } - else { + ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE); + + if (!texture) return GL_FALSE; + + t = _mesa_lookup_texture(ctx, texture); + + /* IsTexture is true only after object has been bound once. */ + return t && t->Target; +} + +/* Simplest implementation of texture locking: Grab the a new mutex in + * the shared context. Examine the shared context state timestamp and + * if there has been a change, set the appropriate bits in + * ctx->NewState. + * + * See also _mesa_lock/unlock_texture in texobj.h + */ +void _mesa_lock_context_textures( GLcontext *ctx ) +{ + _glthread_LOCK_MUTEX(ctx->Shared->TexMutex); + + if (ctx->Shared->TextureStateStamp != ctx->TextureStateTimestamp) { + ctx->NewState |= _NEW_TEXTURE; + ctx->TextureStateTimestamp = ctx->Shared->TextureStateStamp; } } + +void _mesa_unlock_context_textures( GLcontext *ctx ) +{ + assert(ctx->Shared->TextureStateStamp == ctx->TextureStateTimestamp); + _glthread_UNLOCK_MUTEX(ctx->Shared->TexMutex); +} + +/*@}*/ + +