X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Ftexobj.c;h=df64002f994308a8608ee7e46acd06b6936bcd4a;hb=b755a2d9de5b7977c410a904a8adb7c07c88f82a;hp=10fff6467671e06196d6f49bdd08ed32df050764;hpb=c40d1dd62dd9bcbb97128e37a75d991a8d3b2d8c;p=mesa.git diff --git a/src/mesa/main/texobj.c b/src/mesa/main/texobj.c index 10fff646767..df64002f994 100644 --- a/src/mesa/main/texobj.c +++ b/src/mesa/main/texobj.c @@ -5,9 +5,9 @@ /* * Mesa 3-D graphics library - * Version: 5.1 + * Version: 6.5 * - * Copyright (C) 1999-2003 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"), @@ -32,6 +32,7 @@ #include "colortab.h" #include "context.h" #include "enums.h" +#include "fbobject.h" #include "hash.h" #include "imports.h" #include "macros.h" @@ -45,6 +46,19 @@ /** \name Internal functions */ /*@{*/ + +/** + * Return the gl_texture_object for a given ID. + */ +struct gl_texture_object * +_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. @@ -59,25 +73,20 @@ * of GenTextures() * * \return pointer to new texture object. - * - * Allocate and initialize a gl_texture_object structure, and insert in the - * shared state texture list while holding its mutex. - * If name > 0 then also insert the new texture object into the hash - * table. - * */ struct gl_texture_object * _mesa_new_texture_object( GLcontext *ctx, GLuint name, GLenum target ) { struct gl_texture_object *obj; - obj = CALLOC_STRUCT(gl_texture_object); + (void) ctx; + obj = MALLOC_STRUCT(gl_texture_object); _mesa_initialize_texture_object(obj, name, target); return obj; } /** - * Initialize a texture object to default values. + * Initialize a new texture object to default values. * \param obj the texture object * \param name the texture name * \param target the texture target @@ -91,10 +100,12 @@ _mesa_initialize_texture_object( struct gl_texture_object *obj, target == GL_TEXTURE_2D || target == GL_TEXTURE_3D || target == GL_TEXTURE_CUBE_MAP_ARB || - target == GL_TEXTURE_RECTANGLE_NV); + 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 */ - _glthread_INIT_MUTEX(obj->Mutex); obj->RefCount = 1; obj->Name = name; obj->Target = target; @@ -124,104 +135,45 @@ _mesa_initialize_texture_object( struct gl_texture_object *obj, 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. 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. - * - * Unlink the texture object from the shared state texture linked list while - * holding its lock. If the texture is a name number it's also removed from the - * hash table. Finally frees the texture images and the object itself. */ void _mesa_delete_texture_object( GLcontext *ctx, struct gl_texture_object *texObj ) { - GLuint i; + GLuint i, face; (void) ctx; - assert(texObj); - _mesa_free_colortable_data(&texObj->Palette); /* free the texture images */ - for (i = 0; i < MAX_TEXTURE_LEVELS; i++) { - if (texObj->Image[i]) { - _mesa_delete_texture_image( texObj->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] ); + } } } - /* destroy the mutex -- it may have allocated memory (eg on bsd) */ - _glthread_DESTROY_MUTEX(texObj->Mutex); - /* free this object */ _mesa_free(texObj); } -/** - * Add the given texture object to the texture object pool. - */ -void -_mesa_save_texture_object( GLcontext *ctx, struct gl_texture_object *texObj ) -{ - /* insert into linked list */ - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - texObj->Next = ctx->Shared->TexObjectList; - ctx->Shared->TexObjectList = texObj; - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); - - if (texObj->Name > 0) { - /* insert into hash table */ - _mesa_HashInsert(ctx->Shared->TexObjects, texObj->Name, texObj); - } -} - - -/** - * Remove the given texture object from the texture object pool. - * Do not deallocate the texture object though. - */ -void -_mesa_remove_texture_object( GLcontext *ctx, struct gl_texture_object *texObj ) -{ - struct gl_texture_object *tprev, *tcurr; - - _glthread_LOCK_MUTEX(ctx->Shared->Mutex); - - /* unlink from the linked list */ - tprev = NULL; - tcurr = ctx->Shared->TexObjectList; - while (tcurr) { - if (tcurr == texObj) { - if (tprev) { - tprev->Next = texObj->Next; - } - else { - ctx->Shared->TexObjectList = texObj->Next; - } - break; - } - tprev = tcurr; - tcurr = tcurr->Next; - } - - _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); - if (texObj->Name > 0) { - /* remove from hash table */ - _mesa_HashRemove(ctx->Shared->TexObjects, texObj->Name); - } -} /** * Copy texture object state from one texture object to another. + * Use for glPush/PopAttrib. * * \param dest destination texture object. * \param src source texture object. @@ -257,8 +209,7 @@ _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->_IsPowerOfTwo = src->_IsPowerOfTwo; + dest->_Complete = src->_Complete; } @@ -300,44 +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->_IsPowerOfTwo = GL_TRUE; /* may be set FALSE below */ + 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[baseLevel]->Width == 0 || - t->Image[baseLevel]->Height == 0 || - t->Image[baseLevel]->Depth == 0) { + 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; + 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) { @@ -360,34 +315,20 @@ _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; + } } } - /* check for non power of two */ - if (!t->Image[baseLevel]->_IsPowerOfTwo) { - t->_IsPowerOfTwo = GL_FALSE; - } - /* extra checking for mipmaps */ if (t->MinFilter != GL_NEAREST && t->MinFilter != GL_LINEAR) { /* @@ -398,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; } @@ -420,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; } } @@ -444,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; @@ -456,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) { @@ -479,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; @@ -493,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; } } @@ -526,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; @@ -536,33 +479,31 @@ _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; - incomplete(t, "CubeMap Image[i] == NULL"); - return; - } - /* 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 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! */ } } } @@ -583,28 +524,21 @@ _mesa_test_texobj_completeness( const GLcontext *ctx, /** \name API functions */ /*@{*/ -/** - * Texture name generation lock. - * - * Used by _mesa_GenTextures() to guarantee that the generation and allocation - * of texture IDs is atomic. - */ -_glthread_DECLARE_STATIC_MUTEX(GenTexturesLock); /** * Generate texture names. * * \param n number of texture names to be generated. - * \param texName an array in which will hold the generated texture names. + * \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 texName. - * Corresponding empty texture objects are also generated. + * 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 *texName ) +_mesa_GenTextures( GLsizei n, GLuint *textures ) { GET_CURRENT_CONTEXT(ctx); GLuint first; @@ -616,13 +550,13 @@ _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); @@ -633,94 +567,167 @@ _mesa_GenTextures( GLsizei n, GLuint *texName ) GLenum target = 0; texObj = (*ctx->Driver.NewTextureObject)( ctx, name, target); if (!texObj) { + _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTextures"); return; } - _mesa_save_texture_object(ctx, texObj); - texName[i] = name; + + /* 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; + + 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. + */ +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 texName array of textures names to be deleted. + * \param textures array of texture IDs to be deleted. * * \sa glDeleteTextures(). * - * For each texture checks if its bound to any of the texture units, unbinding - * it and decrementing the reference count if so. If the texture reference - * count is zero, delete its object. + * 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 *texName) +_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_IMAGE_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); - _mesa_remove_texture_object(ctx, 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); } @@ -729,6 +736,7 @@ _mesa_DeleteTextures( GLsizei n, const GLuint *texName) } } + /** * Bind a named texture to a texturing target. * @@ -748,16 +756,19 @@ 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; @@ -782,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) @@ -811,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) { @@ -833,6 +870,14 @@ _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 { @@ -842,11 +887,16 @@ _mesa_BindTexture( GLenum target, GLuint texName ) _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindTexture"); return; } - _mesa_save_texture_object(ctx, newTexObj); + + /* 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: @@ -869,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; @@ -878,16 +934,20 @@ _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); - _mesa_remove_texture_object(ctx, oldTexObj); + ASSERT(oldTexObj->Name != 0); ASSERT(ctx->Driver.DeleteTexture); (*ctx->Driver.DeleteTexture)( ctx, oldTexObj ); } } + /** * Set texture priorities. * @@ -918,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) @@ -968,8 +1027,7 @@ _mesa_AreTexturesResident(GLsizei n, const GLuint *texName, _mesa_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident"); return GL_FALSE; } - t = (struct gl_texture_object *) - _mesa_HashLookup(ctx->Shared->TexObjects, texName[i]); + t = _mesa_lookup_texture(ctx, texName[i]); if (!t) { _mesa_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident"); return GL_FALSE; @@ -1009,9 +1067,51 @@ _mesa_AreTexturesResident(GLsizei n, const GLuint *texName, 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); } /*@}*/ + +