5ee145e3cbf0d9232b3d67d80a3c48639b2a773e
[mesa.git] / src / mesa / drivers / glide / fxtexman.c
1 /* -*- mode: C; tab-width:8; -*-
2
3 fxtexman.c - 3Dfx VooDoo texture memory functions
4 */
5
6 /*
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 * See the file fxapi.c for more informations about authors
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "conf.h"
27 #endif
28
29 #if defined(FX)
30
31 #include "fxdrv.h"
32
33 static tfxTMFreeNode *fxTMNewTMFreeNode(FxU32 start, FxU32 end)
34 {
35 tfxTMFreeNode *tmn;
36
37 if(!(tmn=malloc(sizeof(tfxTMFreeNode)))) {
38 fprintf(stderr,"fx Driver: out of memory !\n");
39 fxCloseHardware();
40 exit(-1);
41 }
42
43 tmn->next=NULL;
44 tmn->startAddress=start;
45 tmn->endAddress=end;
46
47 return tmn;
48 }
49
50 static void fxTMUInit(fxMesaContext fxMesa, int tmu)
51 {
52 tfxTMFreeNode *tmn,*tmntmp;
53 FxU32 start,end,blockstart,blockend;
54
55 start=grTexMinAddress(tmu);
56 end=grTexMaxAddress(tmu);
57
58 if(fxMesa->verbose) {
59 fprintf(stderr,"%s configuration:",(tmu==FX_TMU0) ? "TMU0" : "TMU1");
60 fprintf(stderr," Lower texture memory address (%u)\n",(unsigned int)start);
61 fprintf(stderr," Higher texture memory address (%u)\n",(unsigned int)end);
62 fprintf(stderr," Splitting Texture memory in 2Mb blocks:\n");
63 }
64
65 fxMesa->freeTexMem[tmu]=end-start;
66 fxMesa->tmFree[tmu]=NULL;
67 fxMesa->tmAlloc[tmu]=NULL;
68
69 blockstart=start;
70 while(blockstart<=end) {
71 if(blockstart+0x1fffff>end)
72 blockend=end;
73 else
74 blockend=blockstart+0x1fffff;
75
76 if(fxMesa->verbose)
77 fprintf(stderr," %07u-%07u\n",(unsigned int)blockstart,(unsigned int)blockend);
78
79 tmn=fxTMNewTMFreeNode(blockstart,blockend);
80
81 if(fxMesa->tmFree[tmu]) {
82 for(tmntmp=fxMesa->tmFree[tmu];tmntmp->next!=NULL;tmntmp=tmntmp->next){};
83 tmntmp->next=tmn;
84 } else
85 fxMesa->tmFree[tmu]=tmn;
86
87 blockstart+=0x1fffff+1;
88 }
89 }
90
91 void fxTMInit(fxMesaContext fxMesa)
92 {
93 fxTMUInit(fxMesa,FX_TMU0);
94
95 if(fxMesa->haveTwoTMUs)
96 fxTMUInit(fxMesa,FX_TMU1);
97
98 fxMesa->texBindNumber=0;
99 }
100
101 static struct gl_texture_object *fxTMFindOldestTMBlock(fxMesaContext fxMesa,
102 tfxTMAllocNode *tmalloc,
103 GLuint texbindnumber)
104 {
105 GLuint age,oldestage,lasttimeused;
106 struct gl_texture_object *oldesttexobj;
107
108 (void)fxMesa;
109 oldesttexobj=tmalloc->tObj;
110 oldestage=0;
111
112 while(tmalloc) {
113 lasttimeused=((tfxTexInfo *)(tmalloc->tObj->DriverData))->tmi.lastTimeUsed;
114
115 if(lasttimeused>texbindnumber)
116 age=texbindnumber+(UINT_MAX-lasttimeused+1); /* TO DO: check */
117 else
118 age=texbindnumber-lasttimeused;
119
120 if(age>=oldestage) {
121 oldestage=age;
122 oldesttexobj=tmalloc->tObj;
123 }
124
125 tmalloc=tmalloc->next;
126 }
127
128 return oldesttexobj;
129 }
130
131 static GLboolean fxTMFreeOldTMBlock(fxMesaContext fxMesa, GLint tmu)
132 {
133 struct gl_texture_object *oldesttexobj;
134
135 if(!fxMesa->tmAlloc[tmu])
136 return GL_FALSE;
137
138 oldesttexobj=fxTMFindOldestTMBlock(fxMesa,fxMesa->tmAlloc[tmu],fxMesa->texBindNumber);
139
140 fxTMMoveOutTM(fxMesa,oldesttexobj);
141
142 return GL_TRUE;
143 }
144
145 static tfxTMFreeNode *fxTMExtractTMFreeBlock(tfxTMFreeNode *tmfree, int texmemsize,
146 GLboolean *success, FxU32 *startadr)
147 {
148 int blocksize;
149
150 /* TO DO: cut recursion */
151
152 if(!tmfree) {
153 *success=GL_FALSE;
154 return NULL;
155 }
156
157 blocksize=(int)tmfree->endAddress-(int)tmfree->startAddress+1;
158
159 if(blocksize==texmemsize) {
160 tfxTMFreeNode *nexttmfree;
161
162 *success=GL_TRUE;
163 *startadr=tmfree->startAddress;
164
165 nexttmfree=tmfree->next;
166 free(tmfree);
167
168 return nexttmfree;
169 }
170
171 if(blocksize>texmemsize) {
172 *success=GL_TRUE;
173 *startadr=tmfree->startAddress;
174
175 tmfree->startAddress+=texmemsize;
176
177 return tmfree;
178 }
179
180 tmfree->next=fxTMExtractTMFreeBlock(tmfree->next,texmemsize,success,startadr);
181
182 return tmfree;
183 }
184
185 static tfxTMAllocNode *fxTMGetTMBlock(fxMesaContext fxMesa, struct gl_texture_object *tObj,
186 GLint tmu, int texmemsize)
187 {
188 tfxTMFreeNode *newtmfree;
189 tfxTMAllocNode *newtmalloc;
190 GLboolean success;
191 FxU32 startadr;
192
193 for(;;) { /* TO DO: improve performaces */
194 newtmfree=fxTMExtractTMFreeBlock(fxMesa->tmFree[tmu],texmemsize,&success,&startadr);
195
196 if(success) {
197 fxMesa->tmFree[tmu]=newtmfree;
198
199 fxMesa->freeTexMem[tmu]-=texmemsize;
200
201 if(!(newtmalloc=malloc(sizeof(tfxTMAllocNode)))) {
202 fprintf(stderr,"fx Driver: out of memory !\n");
203 fxCloseHardware();
204 exit(-1);
205 }
206
207 newtmalloc->next=fxMesa->tmAlloc[tmu];
208 newtmalloc->startAddress=startadr;
209 newtmalloc->endAddress=startadr+texmemsize-1;
210 newtmalloc->tObj=tObj;
211
212 fxMesa->tmAlloc[tmu]=newtmalloc;
213
214 return newtmalloc;
215 }
216
217 if(!fxTMFreeOldTMBlock(fxMesa,tmu)) {
218 fprintf(stderr,"fx Driver: internal error in fxTMGetTMBlock()\n");
219 fprintf(stderr," TMU: %d Size: %d\n",tmu,texmemsize);
220
221 fxCloseHardware();
222 exit(-1);
223 }
224 }
225 }
226
227 void fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint where)
228 {
229 tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
230 int i,l;
231 int texmemsize;
232
233 if (MESA_VERBOSE&VERBOSE_DRIVER) {
234 fprintf(stderr,"fxmesa: fxTMMoveInTM(%d)\n",tObj->Name);
235 }
236
237 fxMesa->stats.reqTexUpload++;
238
239 if(!ti->validated) {
240 fprintf(stderr,"fx Driver: internal error in fxTMMoveInTM() -> not validated\n");
241 fxCloseHardware();
242 exit(-1);
243 }
244
245 if(ti->tmi.isInTM)
246 return;
247
248 if (MESA_VERBOSE&(VERBOSE_DRIVER|VERBOSE_TEXTURE)) {
249 fprintf(stderr,"fxmesa: downloading %x (%d) in texture memory in %d\n",(GLuint)tObj,tObj->Name,where);
250 }
251
252 ti->tmi.whichTMU=(FxU32)where;
253
254 switch(where) {
255 case FX_TMU0:
256 case FX_TMU1:
257 texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH,&(ti->info));
258 ti->tmi.tm[where]=fxTMGetTMBlock(fxMesa,tObj,where,texmemsize);
259 fxMesa->stats.memTexUpload+=texmemsize;
260
261 for(i=FX_largeLodValue(ti->info),l=ti->minLevel;i<=FX_smallLodValue(ti->info);i++,l++)
262 grTexDownloadMipMapLevel(where,
263 ti->tmi.tm[where]->startAddress,FX_valueToLod(i),
264 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
265 ti->info.format,GR_MIPMAPLEVELMASK_BOTH,
266 ti->tmi.mipmapLevel[l].data);
267 break;
268 case FX_TMU_SPLIT: /* TO DO: alternate even/odd TMU0/TMU1 */
269 texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_ODD,&(ti->info));
270 ti->tmi.tm[FX_TMU0]=fxTMGetTMBlock(fxMesa,tObj,FX_TMU0,texmemsize);
271 fxMesa->stats.memTexUpload+=texmemsize;
272
273 texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_EVEN,&(ti->info));
274 ti->tmi.tm[FX_TMU1]=fxTMGetTMBlock(fxMesa,tObj,FX_TMU1,texmemsize);
275 fxMesa->stats.memTexUpload+=texmemsize;
276
277 for(i=FX_largeLodValue(ti->info),l=ti->minLevel;i<=FX_smallLodValue(ti->info);i++,l++) {
278 grTexDownloadMipMapLevel(GR_TMU0,ti->tmi.tm[FX_TMU0]->startAddress,FX_valueToLod(i),
279 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
280 ti->info.format,GR_MIPMAPLEVELMASK_ODD,
281 ti->tmi.mipmapLevel[l].data);
282
283 grTexDownloadMipMapLevel(GR_TMU1,ti->tmi.tm[FX_TMU1]->startAddress,FX_valueToLod(i),
284 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
285 ti->info.format,GR_MIPMAPLEVELMASK_EVEN,
286 ti->tmi.mipmapLevel[l].data);
287 }
288 break;
289 default:
290 fprintf(stderr,"fx Driver: internal error in fxTMMoveInTM() -> wrong tmu (%d)\n",where);
291 fxCloseHardware();
292 exit(-1);
293 }
294
295 fxMesa->stats.texUpload++;
296
297 ti->tmi.isInTM=GL_TRUE;
298 }
299
300 void fxTMReloadMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint level)
301 {
302 tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
303 GrLOD_t lodlevel;
304 GLint tmu;
305
306 if(!ti->validated) {
307 fprintf(stderr,"fx Driver: internal error in fxTMReloadMipMapLevel() -> not validated\n");
308 fxCloseHardware();
309 exit(-1);
310 }
311
312 tmu=(int)ti->tmi.whichTMU;
313 fxTMMoveInTM(fxMesa,tObj,tmu);
314
315 fxTexGetInfo(ti->tmi.mipmapLevel[0].width,ti->tmi.mipmapLevel[0].height,
316 &lodlevel,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
317
318 switch(tmu) {
319 case FX_TMU0:
320 case FX_TMU1:
321 grTexDownloadMipMapLevel(tmu,
322 ti->tmi.tm[tmu]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
323 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
324 ti->info.format,GR_MIPMAPLEVELMASK_BOTH,
325 ti->tmi.mipmapLevel[level].data);
326 break;
327 case FX_TMU_SPLIT: /* TO DO: alternate even/odd TMU0/TMU1 */
328 grTexDownloadMipMapLevel(GR_TMU0,
329 ti->tmi.tm[GR_TMU0]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
330 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
331 ti->info.format,GR_MIPMAPLEVELMASK_ODD,
332 ti->tmi.mipmapLevel[level].data);
333
334 grTexDownloadMipMapLevel(GR_TMU1,
335 ti->tmi.tm[GR_TMU1]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
336 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
337 ti->info.format,GR_MIPMAPLEVELMASK_EVEN,
338 ti->tmi.mipmapLevel[level].data);
339 break;
340 default:
341 fprintf(stderr,"fx Driver: internal error in fxTMReloadMipMapLevel() -> wrong tmu (%d)\n",tmu);
342 fxCloseHardware();
343 exit(-1);
344 }
345 }
346
347 void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj,
348 GLint level, GLint yoffset, GLint height)
349 {
350 tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
351 GrLOD_t lodlevel;
352 unsigned short *data;
353 GLint tmu;
354
355 if(!ti->validated) {
356 fprintf(stderr,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> not validated\n");
357 fxCloseHardware();
358 exit(-1);
359 }
360
361 tmu=(int)ti->tmi.whichTMU;
362 fxTMMoveInTM(fxMesa,tObj,tmu);
363
364 fxTexGetInfo(ti->tmi.mipmapLevel[0].width,ti->tmi.mipmapLevel[0].height,
365 &lodlevel,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
366
367 if((ti->info.format==GR_TEXFMT_INTENSITY_8) ||
368 (ti->info.format==GR_TEXFMT_P_8) ||
369 (ti->info.format==GR_TEXFMT_ALPHA_8))
370 data=ti->tmi.mipmapLevel[level].data+((yoffset*ti->tmi.mipmapLevel[level].width)>>1);
371 else
372 data=ti->tmi.mipmapLevel[level].data+yoffset*ti->tmi.mipmapLevel[level].width;
373
374 switch(tmu) {
375 case FX_TMU0:
376 case FX_TMU1:
377 grTexDownloadMipMapLevelPartial(tmu,
378 ti->tmi.tm[tmu]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
379 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
380 ti->info.format,GR_MIPMAPLEVELMASK_BOTH,
381 data,
382 yoffset,yoffset+height-1);
383 break;
384 case FX_TMU_SPLIT: /* TO DO: alternate even/odd TMU0/TMU1 */
385 grTexDownloadMipMapLevelPartial(GR_TMU0,
386 ti->tmi.tm[FX_TMU0]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
387 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
388 ti->info.format,GR_MIPMAPLEVELMASK_ODD,
389 data,
390 yoffset,yoffset+height-1);
391
392 grTexDownloadMipMapLevelPartial(GR_TMU1,
393 ti->tmi.tm[FX_TMU1]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
394 FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
395 ti->info.format,GR_MIPMAPLEVELMASK_EVEN,
396 data,
397 yoffset,yoffset+height-1);
398 break;
399 default:
400 fprintf(stderr,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> wrong tmu (%d)\n",tmu);
401 fxCloseHardware();
402 exit(-1);
403 }
404 }
405
406 static tfxTMAllocNode *fxTMFreeTMAllocBlock(tfxTMAllocNode *tmalloc,
407 tfxTMAllocNode *tmunalloc)
408 {
409 if(!tmalloc)
410 return NULL;
411
412 if(tmalloc==tmunalloc) {
413 tfxTMAllocNode *newtmalloc;
414
415 newtmalloc=tmalloc->next;
416 free(tmalloc);
417
418 return newtmalloc;
419 }
420
421 tmalloc->next=fxTMFreeTMAllocBlock(tmalloc->next,tmunalloc);
422
423 return tmalloc;
424 }
425
426 static tfxTMFreeNode *fxTMAddTMFree(tfxTMFreeNode *tmfree, FxU32 startadr, FxU32 endadr)
427 {
428 if(!tmfree)
429 return fxTMNewTMFreeNode(startadr,endadr);
430
431 if((endadr+1==tmfree->startAddress) && (tmfree->startAddress & 0x1fffff)) {
432 tmfree->startAddress=startadr;
433
434 return tmfree;
435 }
436
437 if((startadr-1==tmfree->endAddress) && (startadr & 0x1fffff)) {
438 tmfree->endAddress=endadr;
439
440 if((tmfree->next && (endadr+1==tmfree->next->startAddress) &&
441 (tmfree->next->startAddress & 0x1fffff))) {
442 tfxTMFreeNode *nexttmfree;
443
444 tmfree->endAddress=tmfree->next->endAddress;
445
446 nexttmfree=tmfree->next->next;
447 free(tmfree->next);
448
449 tmfree->next=nexttmfree;
450 }
451
452
453 return tmfree;
454 }
455
456 if(startadr<tmfree->startAddress) {
457 tfxTMFreeNode *newtmfree;
458
459 newtmfree=fxTMNewTMFreeNode(startadr,endadr);
460 newtmfree->next=tmfree;
461
462 return newtmfree;
463 }
464
465 tmfree->next=fxTMAddTMFree(tmfree->next,startadr,endadr);
466
467 return tmfree;
468 }
469
470 static void fxTMFreeTMBlock(fxMesaContext fxMesa, GLint tmu, tfxTMAllocNode *tmalloc)
471 {
472 FxU32 startadr,endadr;
473
474 startadr=tmalloc->startAddress;
475 endadr=tmalloc->endAddress;
476
477 fxMesa->tmAlloc[tmu]=fxTMFreeTMAllocBlock(fxMesa->tmAlloc[tmu],tmalloc);
478
479 fxMesa->tmFree[tmu]=fxTMAddTMFree(fxMesa->tmFree[tmu],startadr,endadr);
480
481 fxMesa->freeTexMem[tmu]+=endadr-startadr+1;
482 }
483
484 void fxTMMoveOutTM(fxMesaContext fxMesa, struct gl_texture_object *tObj)
485 {
486 tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
487
488 if (MESA_VERBOSE&VERBOSE_DRIVER) {
489 fprintf(stderr,"fxmesa: fxTMMoveOutTM(%x (%d))\n",(GLuint)tObj,tObj->Name);
490 }
491
492 if(!ti->tmi.isInTM)
493 return;
494
495 switch(ti->tmi.whichTMU) {
496 case FX_TMU0:
497 case FX_TMU1:
498 fxTMFreeTMBlock(fxMesa,(int)ti->tmi.whichTMU,ti->tmi.tm[ti->tmi.whichTMU]);
499 break;
500 case FX_TMU_SPLIT:
501 fxTMFreeTMBlock(fxMesa,FX_TMU0,ti->tmi.tm[FX_TMU0]);
502 fxTMFreeTMBlock(fxMesa,FX_TMU1,ti->tmi.tm[FX_TMU1]);
503 break;
504 default:
505 fprintf(stderr,"fx Driver: internal error in fxTMMoveOutTM()\n");
506 fxCloseHardware();
507 exit(-1);
508 }
509
510 ti->tmi.whichTMU=FX_TMU_NONE;
511 ti->tmi.isInTM=GL_FALSE;
512 }
513
514 void fxTMFreeTexture(fxMesaContext fxMesa, struct gl_texture_object *tObj)
515 {
516 tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
517 int i;
518
519 fxTMMoveOutTM(fxMesa,tObj);
520
521 for(i=0;i<MAX_TEXTURE_LEVELS;i++) {
522 if(ti->tmi.mipmapLevel[i].used &&
523 ti->tmi.mipmapLevel[i].translated)
524 free(ti->tmi.mipmapLevel[i].data);
525
526 (void)ti->tmi.mipmapLevel[i].data;
527 }
528 }
529
530 void fxTMFreeAllFreeNode(tfxTMFreeNode *fn)
531 {
532 if(!fn)
533 return;
534
535 if(fn->next)
536 fxTMFreeAllFreeNode(fn->next);
537
538 free(fn);
539 }
540
541 void fxTMFreeAllAllocNode(tfxTMAllocNode *an)
542 {
543 if(!an)
544 return;
545
546 if(an->next)
547 fxTMFreeAllAllocNode(an->next);
548
549 free(an);
550 }
551
552 void fxTMClose(fxMesaContext fxMesa)
553 {
554 fxTMFreeAllFreeNode(fxMesa->tmFree[FX_TMU0]);
555 fxTMFreeAllAllocNode(fxMesa->tmAlloc[FX_TMU0]);
556 fxMesa->tmFree[FX_TMU0] = NULL;
557 fxMesa->tmAlloc[FX_TMU0] = NULL;
558 if(fxMesa->haveTwoTMUs) {
559 fxTMFreeAllFreeNode(fxMesa->tmFree[FX_TMU1]);
560 fxTMFreeAllAllocNode(fxMesa->tmAlloc[FX_TMU1]);
561 fxMesa->tmFree[FX_TMU1] = NULL;
562 fxMesa->tmAlloc[FX_TMU1] = NULL;
563 }
564 }
565
566
567 #else
568
569
570 /*
571 * Need this to provide at least one external definition.
572 */
573
574 int gl_fx_dummy_function_texman(void)
575 {
576 return 0;
577 }
578
579 #endif /* FX */