blob: 5ee145e3cbf0d9232b3d67d80a3c48639b2a773e [file] [log] [blame]
/* -*- 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(startadr<tmfree->startAddress) {
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;i<MAX_TEXTURE_LEVELS;i++) {
if(ti->tmi.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 */