X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ftexobj.c;h=df64002f994308a8608ee7e46acd06b6936bcd4a;hb=b755a2d9de5b7977c410a904a8adb7c07c88f82a;hp=93ae40ac745aba1201489d65f6a5d2681b1d8606;hpb=3c63452e64df7e10aa073c6c3b9492b1d7dabbb8;p=mesa.git diff --git a/src/mesa/main/texobj.c b/src/mesa/main/texobj.c index 93ae40ac745..df64002f994 100644 --- a/src/mesa/main/texobj.c +++ b/src/mesa/main/texobj.c @@ -1,10 +1,13 @@ -/* $Id: texobj.c,v 1.62 2002/10/24 23:57:21 brianp Exp $ */ +/** + * \file texobj.c + * Texture object management. + */ /* * Mesa 3-D graphics library - * Version: 4.1 + * Version: 6.5 * - * Copyright (C) 1999-2002 Brian Paul All Rights Reserved. + * 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"), @@ -24,10 +27,12 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + #include "glheader.h" #include "colortab.h" #include "context.h" #include "enums.h" +#include "fbobject.h" #include "hash.h" #include "imports.h" #include "macros.h" @@ -37,140 +42,141 @@ #include "mtypes.h" -/* - * 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 - * 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 +/**********************************************************************/ +/** \name Internal functions */ +/*@{*/ + + +/** + * Return the gl_texture_object for a given ID. */ struct gl_texture_object * -_mesa_alloc_texture_object( struct gl_shared_state *shared, - GLuint name, GLenum target ) +_mesa_lookup_texture(GLcontext *ctx, GLuint id) +{ + return (struct gl_texture_object *) + _mesa_HashLookup(ctx->Shared->TexObjects, id); +} + + + +/** + * 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); - - 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->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->MaxAnisotropy = 1.0; - obj->CompareFlag = GL_FALSE; /* SGIX_shadow */ - obj->CompareOperator = GL_TEXTURE_LEQUAL_R_SGIX; /* SGIX_shadow */ - obj->CompareMode = GL_LUMINANCE; /* 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); - - /* 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); - } + target == GL_TEXTURE_RECTANGLE_NV || + target == GL_TEXTURE_1D_ARRAY_EXT || + target == GL_TEXTURE_2D_ARRAY_EXT); + + _mesa_bzero(obj, sizeof(*obj)); + /* init the non-zero fields */ + 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; } - return obj; + 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->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 */ } -/* - * 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. + * Called via ctx->Driver.DeleteTexture() if not overriden by a driver. + * + * \param shared the shared GL state to which the object belongs. + * \param texOjb the texture object to delete. */ -void _mesa_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; - - assert(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); - } + GLuint i, face; - if (t->Name) { - /* remove from hash table */ - _mesa_HashRemove(shared->TexObjects, t->Name); - } + (void) ctx; - _mesa_free_colortable_data(&t->Palette); + _mesa_free_colortable_data(&texObj->Palette); /* free the texture images */ - { - GLuint i; - for (i=0;iImage[i]) { - _mesa_free_texture_image( t->Image[i] ); - } + 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] ); + } } } /* free this object */ - FREE( t ); + _mesa_free(texObj); } -/* + + +/** * 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, @@ -189,6 +195,7 @@ _mesa_copy_texture_object( struct gl_texture_object *dest, 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; @@ -202,12 +209,17 @@ _mesa_copy_texture_object( struct gl_texture_object *dest, dest->_MaxLambda = src->_MaxLambda; dest->GenerateMipmap = src->GenerateMipmap; dest->Palette = src->Palette; - dest->Complete = src->Complete; + dest->_Complete = src->_Complete; } -/* - * Report why a texture object is incomplete. (for debug only) +/** + * 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 @@ -216,13 +228,21 @@ 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(a, b) +#define incomplete(t, why) #endif -/* +/** * Examine a texture object to determine if it is complete. - * The t->Complete flag will be set to GL_TRUE or GL_FALSE accordingly. + * + * 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, @@ -231,34 +251,48 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, const GLint baseLevel = t->BaseLevel; GLint maxLog2 = 0, maxLevels = 0; - t->Complete = GL_TRUE; /* be optimistic */ + t->_Complete = GL_TRUE; /* be optimistic */ /* Always need the base level image */ - if (!t->Image[baseLevel]) { - incomplete(t, "Image[baseLevel] == NULL"); - t->Complete = GL_FALSE; + 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; + } + + /* 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[baseLevel]->WidthLog2; + if ((t->Target == GL_TEXTURE_1D) || + (t->Target == GL_TEXTURE_1D_ARRAY_EXT)) { + maxLog2 = t->Image[0][baseLevel]->WidthLog2; maxLevels = ctx->Const.MaxTextureLevels; } - else if (t->Target == GL_TEXTURE_2D) { - maxLog2 = MAX2(t->Image[baseLevel]->WidthLog2, - t->Image[baseLevel]->HeightLog2); + else if ((t->Target == GL_TEXTURE_2D) || + (t->Target == GL_TEXTURE_2D_ARRAY_EXT)) { + maxLog2 = MAX2(t->Image[0][baseLevel]->WidthLog2, + t->Image[0][baseLevel]->HeightLog2); maxLevels = ctx->Const.MaxTextureLevels; } else if (t->Target == GL_TEXTURE_3D) { - GLint max = MAX2(t->Image[baseLevel]->WidthLog2, - t->Image[baseLevel]->HeightLog2); - maxLog2 = MAX2(max, (GLint)(t->Image[baseLevel]->DepthLog2)); + 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->Target == GL_TEXTURE_CUBE_MAP_ARB) { - maxLog2 = MAX2(t->Image[baseLevel]->WidthLog2, - t->Image[baseLevel]->HeightLog2); + maxLog2 = MAX2(t->Image[0][baseLevel]->WidthLog2, + t->Image[0][baseLevel]->HeightLog2); maxLevels = ctx->Const.MaxCubeTextureLevels; } else if (t->Target == GL_TEXTURE_RECTANGLE_NV) { @@ -281,29 +315,21 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, 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[baseLevel]->Width2; - const GLuint 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; - incomplete(t, "Non-quare cubemap image"); - return; + 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 @@ -313,21 +339,21 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, GLint maxLevel = t->_MaxLevel; if (minLevel > maxLevel) { - t->Complete = GL_FALSE; + 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]->TexFormat != t->Image[baseLevel]->TexFormat) { - t->Complete = GL_FALSE; + 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) { - t->Complete = GL_FALSE; + if (t->Image[0][i]->Border != t->Image[0][baseLevel]->Border) { + t->_Complete = GL_FALSE; incomplete(t, "Border[i] != Border[baseLevel]"); return; } @@ -335,22 +361,23 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, } /* Test things which depend on number of texture image dimensions */ - if (t->Target == GL_TEXTURE_1D) { + if ((t->Target == GL_TEXTURE_1D) || + (t->Target == GL_TEXTURE_1D_ARRAY_EXT)) { /* Test 1-D mipmaps */ - GLuint width = t->Image[baseLevel]->Width2; + 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]) { - t->Complete = GL_FALSE; - incomplete(t, "1D Image[i] == NULL"); + if (!t->Image[0][i]) { + t->_Complete = GL_FALSE; + incomplete(t, "1D Image[0][i] == NULL"); return; } - if (t->Image[i]->Width2 != width ) { - t->Complete = GL_FALSE; - incomplete(t, "1D Image[i] bad width"); + if (t->Image[0][i]->Width2 != width ) { + t->_Complete = GL_FALSE; + incomplete(t, "1D Image[0][i] bad width"); return; } } @@ -359,10 +386,11 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, } } } - else if (t->Target == GL_TEXTURE_2D) { + else if ((t->Target == GL_TEXTURE_2D) || + (t->Target == GL_TEXTURE_2D_ARRAY_EXT)) { /* Test 2-D mipmaps */ - GLuint width = t->Image[baseLevel]->Width2; - GLuint height = t->Image[baseLevel]->Height2; + 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; @@ -371,19 +399,19 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, height /= 2; } if (i >= minLevel && i <= maxLevel) { - if (!t->Image[i]) { - t->Complete = GL_FALSE; - incomplete(t, "2D Image[i] == NULL"); + if (!t->Image[0][i]) { + t->_Complete = GL_FALSE; + incomplete(t, "2D Image[0][i] == NULL"); return; } - if (t->Image[i]->Width2 != width) { - t->Complete = GL_FALSE; - incomplete(t, "2D Image[i] bad 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) { - t->Complete = GL_FALSE; - incomplete(t, "2D Image[i] bad 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) { @@ -394,9 +422,9 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, } 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; + 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; @@ -408,29 +436,29 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, depth /= 2; } if (i >= minLevel && i <= maxLevel) { - if (!t->Image[i]) { - incomplete(t, "3D Image[i] == NULL"); - t->Complete = GL_FALSE; + if (!t->Image[0][i]) { + incomplete(t, "3D Image[0][i] == NULL"); + t->_Complete = GL_FALSE; return; } - if (t->Image[i]->Format == GL_DEPTH_COMPONENT) { - t->Complete = GL_FALSE; + 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) { - t->Complete = GL_FALSE; - incomplete(t, "3D Image[i] bad 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) { - t->Complete = GL_FALSE; - incomplete(t, "3D Image[i] bad 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) { - t->Complete = GL_FALSE; - incomplete(t, "3D Image[i] bad depth"); + if (t->Image[0][i]->Depth2 != depth) { + t->_Complete = GL_FALSE; + incomplete(t, "3D Image[0][i] bad depth"); return; } } @@ -441,8 +469,8 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, } 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; + 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; @@ -451,39 +479,36 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, height /= 2; } if (i >= minLevel && i <= maxLevel) { - /* Don't support GL_DEPTH_COMPONENT for cube maps */ - if (t->Image[i]->Format == GL_DEPTH_COMPONENT) { - t->Complete = GL_FALSE; - incomplete(t, "GL_DEPTH_COMPONENT only works with 1/2D tex"); - return; - } - /* 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; - incomplete(t, "CubeMap Image[i] == NULL"); - 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; - incomplete(t, "CubeMap Image[i] bad size"); - 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 { /* Target = ??? */ @@ -492,15 +517,28 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, } } +/*@}*/ -_glthread_DECLARE_STATIC_MUTEX(GenTexturesLock); +/***********************************************************************/ +/** \name API functions */ +/*@{*/ -/* - * Execute glGenTextures - */ -void -_mesa_GenTextures( GLsizei n, GLuint *texName ) + +/** + * 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 ) { GET_CURRENT_CONTEXT(ctx); GLuint first; @@ -512,104 +550,186 @@ _mesa_GenTextures( GLsizei n, GLuint *texName ) return; } - if (!texName) + if (!textures) return; /* * This must be atomic (generation and allocation of texture IDs) */ - _glthread_LOCK_MUTEX(GenTexturesLock); + _glthread_LOCK_MUTEX(ctx->Shared->Mutex); first = _mesa_HashFindFreeKeyBlock(ctx->Shared->TexObjects, n); - /* Return the texture names */ - for (i=0;iShared, name, target); + texObj = (*ctx->Driver.NewTextureObject)( ctx, name, target); + if (!texObj) { + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTextures"); + return; + } + + /* insert into hash table */ + _mesa_HashInsert(ctx->Shared->TexObjects, texObj->Name, texObj); + + textures[i] = name; } - _glthread_UNLOCK_MUTEX(GenTexturesLock); + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); } +/** + * 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]; + struct gl_texture_object **curr = NULL; + + if (texObj == unit->Current1D) { + curr = &unit->Current1D; + unit->Current1D = ctx->Shared->Default1D; + } + else if (texObj == unit->Current2D) { + curr = &unit->Current2D; + unit->Current2D = ctx->Shared->Default2D; + } + else if (texObj == unit->Current3D) { + curr = &unit->Current3D; + unit->Current3D = ctx->Shared->Default3D; + } + else if (texObj == unit->CurrentCubeMap) { + curr = &unit->CurrentCubeMap; + unit->CurrentCubeMap = ctx->Shared->DefaultCubeMap; + } + else if (texObj == unit->CurrentRect) { + curr = &unit->CurrentRect; + unit->CurrentRect = ctx->Shared->DefaultRect; + } + else if (texObj == unit->Current1DArray) { + curr = &unit->Current1DArray; + unit->CurrentRect = ctx->Shared->Default1DArray; + } + else if (texObj == unit->Current2DArray) { + curr = &unit->Current1DArray; + unit->CurrentRect = ctx->Shared->Default2DArray; + } + + if (curr) { + (*curr)->RefCount++; + texObj->RefCount--; + if (texObj == unit->_Current) + unit->_Current = *curr; + } + } +} + + +/** + * 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 */ - if (!texName) + if (!textures) return; - for (i=0;i 0) { - struct gl_texture_object *delObj = (struct gl_texture_object *) - _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]); + for (i = 0; i < n; i++) { + if (textures[i] > 0) { + struct gl_texture_object *delObj + = _mesa_lookup_texture(ctx, textures[i]); + if (delObj) { - /* First check if this texture is currently bound. + 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]; - if (delObj == unit->Current1D) { - unit->Current1D = ctx->Shared->Default1D; - ctx->Shared->Default1D->RefCount++; - delObj->RefCount--; - if (delObj == unit->_Current) - unit->_Current = unit->Current1D; - } - else if (delObj == unit->Current2D) { - unit->Current2D = ctx->Shared->Default2D; - ctx->Shared->Default2D->RefCount++; - delObj->RefCount--; - if (delObj == unit->_Current) - unit->_Current = unit->Current2D; - } - else if (delObj == unit->Current3D) { - unit->Current3D = ctx->Shared->Default3D; - ctx->Shared->Default3D->RefCount++; - delObj->RefCount--; - if (delObj == unit->_Current) - unit->_Current = unit->Current3D; - } - else if (delObj == unit->CurrentCubeMap) { - unit->CurrentCubeMap = ctx->Shared->DefaultCubeMap; - ctx->Shared->DefaultCubeMap->RefCount++; - delObj->RefCount--; - if (delObj == unit->_Current) - unit->_Current = unit->CurrentCubeMap; - } - else if (delObj == unit->CurrentRect) { - unit->CurrentRect = ctx->Shared->DefaultRect; - ctx->Shared->DefaultRect->RefCount++; - delObj->RefCount--; - if (delObj == unit->_Current) - unit->_Current = unit->CurrentRect; - } - } + unbind_texobj_from_texunits(ctx, delObj); + ctx->NewState |= _NEW_TEXTURE; - /* Decrement reference count and delete if zero */ - delObj->RefCount--; - ASSERT(delObj->RefCount >= 0); + /* 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); - if (delObj->RefCount == 0) { - ASSERT(delObj->Name != 0); - if (ctx->Driver.DeleteTexture) - (*ctx->Driver.DeleteTexture)( ctx, delObj ); - _mesa_free_texture_object(ctx->Shared, delObj); + /* 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); } } } @@ -617,24 +737,38 @@ _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 = 0; + struct gl_texture_object *newTexObj = NULL; ASSERT_OUTSIDE_BEGIN_END(ctx); if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE)) _mesa_debug(ctx, "glBindTexture %s %d\n", _mesa_lookup_enum_by_nr(target), (GLint) texName); + /* + * Get pointer to currently bound texture object (oldTexObj) + */ switch (target) { case GL_TEXTURE_1D: oldTexObj = texUnit->Current1D; @@ -659,13 +793,34 @@ _mesa_BindTexture( GLenum target, GLuint texName ) } oldTexObj = texUnit->CurrentRect; break; + case GL_TEXTURE_1D_ARRAY_EXT: + if (!ctx->Extensions.MESA_texture_array) { + _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" ); + return; + } + oldTexObj = texUnit->Current1DArray; + break; + case GL_TEXTURE_2D_ARRAY_EXT: + if (!ctx->Extensions.MESA_texture_array) { + _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" ); + return; + } + oldTexObj = texUnit->Current2DArray; + break; default: _mesa_error( ctx, GL_INVALID_ENUM, "glBindTexture(target)" ); return; } - if (oldTexObj->Name == texName) + 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) @@ -688,14 +843,19 @@ _mesa_BindTexture( GLenum target, GLuint texName ) case GL_TEXTURE_RECTANGLE_NV: newTexObj = ctx->Shared->DefaultRect; break; + case GL_TEXTURE_1D_ARRAY_EXT: + newTexObj = ctx->Shared->Default1DArray; + break; + case GL_TEXTURE_2D_ARRAY_EXT: + newTexObj = ctx->Shared->Default2DArray; + break; default: ; /* Bad targets are caught above */ } } else { /* non-default texture object */ - const struct _mesa_HashTable *hash = ctx->Shared->TexObjects; - newTexObj = (struct gl_texture_object *) _mesa_HashLookup(hash, texName); + newTexObj = _mesa_lookup_texture(ctx, texName); if (newTexObj) { /* error checking */ if (newTexObj->Target != 0 && newTexObj->Target != target) { @@ -710,20 +870,33 @@ _mesa_BindTexture( GLenum target, GLuint texName ) 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 = _mesa_alloc_texture_object( ctx->Shared, texName, - target); + 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: @@ -746,6 +919,12 @@ _mesa_BindTexture( GLenum target, GLuint texName ) case GL_TEXTURE_RECTANGLE_NV: texUnit->CurrentRect = newTexObj; break; + case GL_TEXTURE_1D_ARRAY_EXT: + texUnit->Current1DArray = newTexObj; + break; + case GL_TEXTURE_2D_ARRAY_EXT: + texUnit->Current2DArray = newTexObj; + break; default: _mesa_problem(ctx, "bad target in BindTexture"); return; @@ -755,23 +934,33 @@ _mesa_BindTexture( GLenum target, GLuint texName ) if (ctx->Driver.BindTexture) (*ctx->Driver.BindTexture)( ctx, target, newTexObj ); + /* Decrement the reference count on the old texture and check if it's + * time to delete it. + */ + /* XXX all RefCount accesses should be protected by a mutex. */ oldTexObj->RefCount--; - assert(oldTexObj->RefCount >= 0); + ASSERT(oldTexObj->RefCount >= 0); if (oldTexObj->RefCount == 0) { - assert(oldTexObj->Name != 0); - if (ctx->Driver.DeleteTexture) { - (*ctx->Driver.DeleteTexture)( ctx, oldTexObj ); - } - _mesa_free_texture_object(ctx->Shared, oldTexObj); + 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 ) { @@ -789,8 +978,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) @@ -802,18 +990,27 @@ _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); if (n < 0) { @@ -827,38 +1024,94 @@ _mesa_AreTexturesResident(GLsizei n, const GLuint *texName, for (i = 0; i < n; i++) { struct gl_texture_object *t; if (texName[i] == 0) { - _mesa_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 { - _mesa_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_WITH_RETVAL(ctx, GL_FALSE); - return texture > 0 && _mesa_HashLookup(ctx->Shared->TexObjects, texture); + + 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. + * + * This is used to deal with synchronizing things when a texture object + * is used/modified by different contexts (or threads) which are sharing + * the texture. + * + * See also _mesa_lock/unlock_texture() in teximage.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); } + +/*@}*/ + +