/* -*- mode: C; tab-width:8; -*- fxtexman.c - 3Dfx VooDoo texture memory functions */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * See the file fxapi.c for more informations about authors * */ #ifdef HAVE_CONFIG_H #include "conf.h" #endif #if defined(FX) #include "fxdrv.h" static tfxTMFreeNode *fxTMNewTMFreeNode(FxU32 start, FxU32 end) { tfxTMFreeNode *tmn; if(!(tmn=malloc(sizeof(tfxTMFreeNode)))) { fprintf(stderr,"fx Driver: out of memory !\n"); fxCloseHardware(); exit(-1); } tmn->next=NULL; tmn->startAddress=start; tmn->endAddress=end; return tmn; } static void fxTMUInit(fxMesaContext fxMesa, int tmu) { tfxTMFreeNode *tmn,*tmntmp; FxU32 start,end,blockstart,blockend; start=grTexMinAddress(tmu); end=grTexMaxAddress(tmu); if(fxMesa->verbose) { fprintf(stderr,"%s configuration:",(tmu==FX_TMU0) ? "TMU0" : "TMU1"); fprintf(stderr," Lower texture memory address (%u)\n",(unsigned int)start); fprintf(stderr," Higher texture memory address (%u)\n",(unsigned int)end); fprintf(stderr," Splitting Texture memory in 2Mb blocks:\n"); } fxMesa->freeTexMem[tmu]=end-start; fxMesa->tmFree[tmu]=NULL; fxMesa->tmAlloc[tmu]=NULL; blockstart=start; while(blockstart<=end) { if(blockstart+0x1fffff>end) blockend=end; else blockend=blockstart+0x1fffff; if(fxMesa->verbose) fprintf(stderr," %07u-%07u\n",(unsigned int)blockstart,(unsigned int)blockend); tmn=fxTMNewTMFreeNode(blockstart,blockend); if(fxMesa->tmFree[tmu]) { for(tmntmp=fxMesa->tmFree[tmu];tmntmp->next!=NULL;tmntmp=tmntmp->next){}; tmntmp->next=tmn; } else fxMesa->tmFree[tmu]=tmn; blockstart+=0x1fffff+1; } } void fxTMInit(fxMesaContext fxMesa) { fxTMUInit(fxMesa,FX_TMU0); if(fxMesa->haveTwoTMUs) fxTMUInit(fxMesa,FX_TMU1); fxMesa->texBindNumber=0; } static struct gl_texture_object *fxTMFindOldestTMBlock(fxMesaContext fxMesa, tfxTMAllocNode *tmalloc, GLuint texbindnumber) { GLuint age,oldestage,lasttimeused; struct gl_texture_object *oldesttexobj; (void)fxMesa; oldesttexobj=tmalloc->tObj; oldestage=0; while(tmalloc) { lasttimeused=((tfxTexInfo *)(tmalloc->tObj->DriverData))->tmi.lastTimeUsed; if(lasttimeused>texbindnumber) age=texbindnumber+(UINT_MAX-lasttimeused+1); /* TO DO: check */ else age=texbindnumber-lasttimeused; if(age>=oldestage) { oldestage=age; oldesttexobj=tmalloc->tObj; } tmalloc=tmalloc->next; } return oldesttexobj; } static GLboolean fxTMFreeOldTMBlock(fxMesaContext fxMesa, GLint tmu) { struct gl_texture_object *oldesttexobj; if(!fxMesa->tmAlloc[tmu]) return GL_FALSE; oldesttexobj=fxTMFindOldestTMBlock(fxMesa,fxMesa->tmAlloc[tmu],fxMesa->texBindNumber); fxTMMoveOutTM(fxMesa,oldesttexobj); return GL_TRUE; } static tfxTMFreeNode *fxTMExtractTMFreeBlock(tfxTMFreeNode *tmfree, int texmemsize, GLboolean *success, FxU32 *startadr) { int blocksize; /* TO DO: cut recursion */ if(!tmfree) { *success=GL_FALSE; return NULL; } blocksize=(int)tmfree->endAddress-(int)tmfree->startAddress+1; if(blocksize==texmemsize) { tfxTMFreeNode *nexttmfree; *success=GL_TRUE; *startadr=tmfree->startAddress; nexttmfree=tmfree->next; free(tmfree); return nexttmfree; } if(blocksize>texmemsize) { *success=GL_TRUE; *startadr=tmfree->startAddress; tmfree->startAddress+=texmemsize; return tmfree; } tmfree->next=fxTMExtractTMFreeBlock(tmfree->next,texmemsize,success,startadr); return tmfree; } static tfxTMAllocNode *fxTMGetTMBlock(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint tmu, int texmemsize) { tfxTMFreeNode *newtmfree; tfxTMAllocNode *newtmalloc; GLboolean success; FxU32 startadr; for(;;) { /* TO DO: improve performaces */ newtmfree=fxTMExtractTMFreeBlock(fxMesa->tmFree[tmu],texmemsize,&success,&startadr); if(success) { fxMesa->tmFree[tmu]=newtmfree; fxMesa->freeTexMem[tmu]-=texmemsize; if(!(newtmalloc=malloc(sizeof(tfxTMAllocNode)))) { fprintf(stderr,"fx Driver: out of memory !\n"); fxCloseHardware(); exit(-1); } newtmalloc->next=fxMesa->tmAlloc[tmu]; newtmalloc->startAddress=startadr; newtmalloc->endAddress=startadr+texmemsize-1; newtmalloc->tObj=tObj; fxMesa->tmAlloc[tmu]=newtmalloc; return newtmalloc; } if(!fxTMFreeOldTMBlock(fxMesa,tmu)) { fprintf(stderr,"fx Driver: internal error in fxTMGetTMBlock()\n"); fprintf(stderr," TMU: %d Size: %d\n",tmu,texmemsize); fxCloseHardware(); exit(-1); } } } void fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint where) { tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData; int i,l; int texmemsize; if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxTMMoveInTM(%d)\n",tObj->Name); } fxMesa->stats.reqTexUpload++; if(!ti->validated) { fprintf(stderr,"fx Driver: internal error in fxTMMoveInTM() -> not validated\n"); fxCloseHardware(); exit(-1); } if(ti->tmi.isInTM) return; if (MESA_VERBOSE&(VERBOSE_DRIVER|VERBOSE_TEXTURE)) { fprintf(stderr,"fxmesa: downloading %x (%d) in texture memory in %d\n",(GLuint)tObj,tObj->Name,where); } ti->tmi.whichTMU=(FxU32)where; switch(where) { case FX_TMU0: case FX_TMU1: texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH,&(ti->info)); ti->tmi.tm[where]=fxTMGetTMBlock(fxMesa,tObj,where,texmemsize); fxMesa->stats.memTexUpload+=texmemsize; for(i=FX_largeLodValue(ti->info),l=ti->minLevel;i<=FX_smallLodValue(ti->info);i++,l++) grTexDownloadMipMapLevel(where, ti->tmi.tm[where]->startAddress,FX_valueToLod(i), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_BOTH, ti->tmi.mipmapLevel[l].data); break; case FX_TMU_SPLIT: /* TO DO: alternate even/odd TMU0/TMU1 */ texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_ODD,&(ti->info)); ti->tmi.tm[FX_TMU0]=fxTMGetTMBlock(fxMesa,tObj,FX_TMU0,texmemsize); fxMesa->stats.memTexUpload+=texmemsize; texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_EVEN,&(ti->info)); ti->tmi.tm[FX_TMU1]=fxTMGetTMBlock(fxMesa,tObj,FX_TMU1,texmemsize); fxMesa->stats.memTexUpload+=texmemsize; for(i=FX_largeLodValue(ti->info),l=ti->minLevel;i<=FX_smallLodValue(ti->info);i++,l++) { grTexDownloadMipMapLevel(GR_TMU0,ti->tmi.tm[FX_TMU0]->startAddress,FX_valueToLod(i), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_ODD, ti->tmi.mipmapLevel[l].data); grTexDownloadMipMapLevel(GR_TMU1,ti->tmi.tm[FX_TMU1]->startAddress,FX_valueToLod(i), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_EVEN, ti->tmi.mipmapLevel[l].data); } break; default: fprintf(stderr,"fx Driver: internal error in fxTMMoveInTM() -> wrong tmu (%d)\n",where); fxCloseHardware(); exit(-1); } fxMesa->stats.texUpload++; ti->tmi.isInTM=GL_TRUE; } void fxTMReloadMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint level) { tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData; GrLOD_t lodlevel; GLint tmu; if(!ti->validated) { fprintf(stderr,"fx Driver: internal error in fxTMReloadMipMapLevel() -> not validated\n"); fxCloseHardware(); exit(-1); } tmu=(int)ti->tmi.whichTMU; fxTMMoveInTM(fxMesa,tObj,tmu); fxTexGetInfo(ti->tmi.mipmapLevel[0].width,ti->tmi.mipmapLevel[0].height, &lodlevel,NULL,NULL,NULL,NULL,NULL,NULL,NULL); switch(tmu) { case FX_TMU0: case FX_TMU1: grTexDownloadMipMapLevel(tmu, ti->tmi.tm[tmu]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_BOTH, ti->tmi.mipmapLevel[level].data); break; case FX_TMU_SPLIT: /* TO DO: alternate even/odd TMU0/TMU1 */ grTexDownloadMipMapLevel(GR_TMU0, ti->tmi.tm[GR_TMU0]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_ODD, ti->tmi.mipmapLevel[level].data); grTexDownloadMipMapLevel(GR_TMU1, ti->tmi.tm[GR_TMU1]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_EVEN, ti->tmi.mipmapLevel[level].data); break; default: fprintf(stderr,"fx Driver: internal error in fxTMReloadMipMapLevel() -> wrong tmu (%d)\n",tmu); fxCloseHardware(); exit(-1); } } void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint level, GLint yoffset, GLint height) { tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData; GrLOD_t lodlevel; unsigned short *data; GLint tmu; if(!ti->validated) { fprintf(stderr,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> not validated\n"); fxCloseHardware(); exit(-1); } tmu=(int)ti->tmi.whichTMU; fxTMMoveInTM(fxMesa,tObj,tmu); fxTexGetInfo(ti->tmi.mipmapLevel[0].width,ti->tmi.mipmapLevel[0].height, &lodlevel,NULL,NULL,NULL,NULL,NULL,NULL,NULL); if((ti->info.format==GR_TEXFMT_INTENSITY_8) || (ti->info.format==GR_TEXFMT_P_8) || (ti->info.format==GR_TEXFMT_ALPHA_8)) data=ti->tmi.mipmapLevel[level].data+((yoffset*ti->tmi.mipmapLevel[level].width)>>1); else data=ti->tmi.mipmapLevel[level].data+yoffset*ti->tmi.mipmapLevel[level].width; switch(tmu) { case FX_TMU0: case FX_TMU1: grTexDownloadMipMapLevelPartial(tmu, ti->tmi.tm[tmu]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_BOTH, data, yoffset,yoffset+height-1); break; case FX_TMU_SPLIT: /* TO DO: alternate even/odd TMU0/TMU1 */ grTexDownloadMipMapLevelPartial(GR_TMU0, ti->tmi.tm[FX_TMU0]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_ODD, data, yoffset,yoffset+height-1); grTexDownloadMipMapLevelPartial(GR_TMU1, ti->tmi.tm[FX_TMU1]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level), FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info), ti->info.format,GR_MIPMAPLEVELMASK_EVEN, data, yoffset,yoffset+height-1); break; default: fprintf(stderr,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> wrong tmu (%d)\n",tmu); fxCloseHardware(); exit(-1); } } static tfxTMAllocNode *fxTMFreeTMAllocBlock(tfxTMAllocNode *tmalloc, tfxTMAllocNode *tmunalloc) { if(!tmalloc) return NULL; if(tmalloc==tmunalloc) { tfxTMAllocNode *newtmalloc; newtmalloc=tmalloc->next; free(tmalloc); return newtmalloc; } tmalloc->next=fxTMFreeTMAllocBlock(tmalloc->next,tmunalloc); return tmalloc; } static tfxTMFreeNode *fxTMAddTMFree(tfxTMFreeNode *tmfree, FxU32 startadr, FxU32 endadr) { if(!tmfree) return fxTMNewTMFreeNode(startadr,endadr); if((endadr+1==tmfree->startAddress) && (tmfree->startAddress & 0x1fffff)) { tmfree->startAddress=startadr; return tmfree; } if((startadr-1==tmfree->endAddress) && (startadr & 0x1fffff)) { tmfree->endAddress=endadr; if((tmfree->next && (endadr+1==tmfree->next->startAddress) && (tmfree->next->startAddress & 0x1fffff))) { tfxTMFreeNode *nexttmfree; tmfree->endAddress=tmfree->next->endAddress; nexttmfree=tmfree->next->next; free(tmfree->next); tmfree->next=nexttmfree; } return tmfree; } if(startadrstartAddress) { tfxTMFreeNode *newtmfree; newtmfree=fxTMNewTMFreeNode(startadr,endadr); newtmfree->next=tmfree; return newtmfree; } tmfree->next=fxTMAddTMFree(tmfree->next,startadr,endadr); return tmfree; } static void fxTMFreeTMBlock(fxMesaContext fxMesa, GLint tmu, tfxTMAllocNode *tmalloc) { FxU32 startadr,endadr; startadr=tmalloc->startAddress; endadr=tmalloc->endAddress; fxMesa->tmAlloc[tmu]=fxTMFreeTMAllocBlock(fxMesa->tmAlloc[tmu],tmalloc); fxMesa->tmFree[tmu]=fxTMAddTMFree(fxMesa->tmFree[tmu],startadr,endadr); fxMesa->freeTexMem[tmu]+=endadr-startadr+1; } void fxTMMoveOutTM(fxMesaContext fxMesa, struct gl_texture_object *tObj) { tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData; if (MESA_VERBOSE&VERBOSE_DRIVER) { fprintf(stderr,"fxmesa: fxTMMoveOutTM(%x (%d))\n",(GLuint)tObj,tObj->Name); } if(!ti->tmi.isInTM) return; switch(ti->tmi.whichTMU) { case FX_TMU0: case FX_TMU1: fxTMFreeTMBlock(fxMesa,(int)ti->tmi.whichTMU,ti->tmi.tm[ti->tmi.whichTMU]); break; case FX_TMU_SPLIT: fxTMFreeTMBlock(fxMesa,FX_TMU0,ti->tmi.tm[FX_TMU0]); fxTMFreeTMBlock(fxMesa,FX_TMU1,ti->tmi.tm[FX_TMU1]); break; default: fprintf(stderr,"fx Driver: internal error in fxTMMoveOutTM()\n"); fxCloseHardware(); exit(-1); } ti->tmi.whichTMU=FX_TMU_NONE; ti->tmi.isInTM=GL_FALSE; } void fxTMFreeTexture(fxMesaContext fxMesa, struct gl_texture_object *tObj) { tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData; int i; fxTMMoveOutTM(fxMesa,tObj); for(i=0;itmi.mipmapLevel[i].used && ti->tmi.mipmapLevel[i].translated) free(ti->tmi.mipmapLevel[i].data); (void)ti->tmi.mipmapLevel[i].data; } } void fxTMFreeAllFreeNode(tfxTMFreeNode *fn) { if(!fn) return; if(fn->next) fxTMFreeAllFreeNode(fn->next); free(fn); } void fxTMFreeAllAllocNode(tfxTMAllocNode *an) { if(!an) return; if(an->next) fxTMFreeAllAllocNode(an->next); free(an); } void fxTMClose(fxMesaContext fxMesa) { fxTMFreeAllFreeNode(fxMesa->tmFree[FX_TMU0]); fxTMFreeAllAllocNode(fxMesa->tmAlloc[FX_TMU0]); fxMesa->tmFree[FX_TMU0] = NULL; fxMesa->tmAlloc[FX_TMU0] = NULL; if(fxMesa->haveTwoTMUs) { fxTMFreeAllFreeNode(fxMesa->tmFree[FX_TMU1]); fxTMFreeAllAllocNode(fxMesa->tmAlloc[FX_TMU1]); fxMesa->tmFree[FX_TMU1] = NULL; fxMesa->tmAlloc[FX_TMU1] = NULL; } } #else /* * Need this to provide at least one external definition. */ int gl_fx_dummy_function_texman(void) { return 0; } #endif /* FX */