Before calling _mesa_create_context(), initialize a dd_function_table struct
[mesa.git] / src / mesa / drivers / dri / tdfx / tdfx_texman.c
1 /* -*- mode: c; c-basic-offset: 3 -*-
2 *
3 * Copyright 2000 VA Linux Systems Inc., Fremont, California.
4 *
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * VA LINUX SYSTEMS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
23 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26 /* $XFree86: xc/lib/GL/mesa/src/drv/tdfx/tdfx_texman.c,v 1.5 2002/02/22 21:45:04 dawes Exp $ */
27
28 /*
29 * Original rewrite:
30 * Gareth Hughes <gareth@valinux.com>, 29 Sep - 1 Oct 2000
31 *
32 * Authors:
33 * Gareth Hughes <gareth@valinux.com>
34 * Brian Paul <brianp@valinux.com>
35 *
36 */
37
38 #include "tdfx_context.h"
39 #include "tdfx_tex.h"
40 #include "tdfx_texman.h"
41
42
43 #define BAD_ADDRESS ((FxU32) -1)
44
45
46 #if 0 /* DEBUG use */
47 /*
48 * Verify the consistancy of the texture memory manager.
49 * This involves:
50 * Traversing all texture objects and computing total memory used.
51 * Traverse the free block list and computing total memory free.
52 * Compare the total free and total used amounts to the total memory size.
53 * Make various assertions about the results.
54 */
55 static void
56 VerifyFreeList(tdfxContextPtr fxMesa, FxU32 tmu)
57 {
58 struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
59 struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
60 tdfxMemRange *block;
61 int prevStart = -1, prevEnd = -1;
62 int totalFree = 0;
63 int numObj = 0, numRes = 0;
64 int totalUsed = 0;
65
66 for (block = shared->tmFree[tmu]; block; block = block->next) {
67 assert( block->endAddr > 0 );
68 assert( block->startAddr <= shared->totalTexMem[tmu] );
69 assert( block->endAddr <= shared->totalTexMem[tmu] );
70 assert( (int) block->startAddr > prevStart );
71 assert( (int) block->startAddr >= prevEnd );
72 prevStart = (int) block->startAddr;
73 prevEnd = (int) block->endAddr;
74 totalFree += (block->endAddr - block->startAddr);
75 }
76 assert(totalFree == shared->freeTexMem[tmu]);
77
78 {
79 struct gl_texture_object *obj;
80 for (obj = mesaShared->TexObjectList; obj; obj = obj->Next) {
81 tdfxTexInfo *ti = TDFX_TEXTURE_DATA(obj);
82 numObj++;
83 if (ti) {
84 if (ti->isInTM) {
85 numRes++;
86 assert(ti->tm[0]);
87 if (ti->tm[tmu])
88 totalUsed += (ti->tm[tmu]->endAddr - ti->tm[tmu]->startAddr);
89 }
90 else {
91 assert(!ti->tm[0]);
92 }
93 }
94 }
95 }
96
97 printf("totalFree: %d totalUsed: %d totalMem: %d #objs=%d #res=%d\n",
98 shared->freeTexMem[tmu], totalUsed, shared->totalTexMem[tmu],
99 numObj, numRes);
100
101 assert(totalUsed + totalFree == shared->totalTexMem[tmu]);
102 }
103
104
105 static void
106 dump_texmem(tdfxContextPtr fxMesa)
107 {
108 struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
109 struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
110 struct gl_texture_object *oldestObj, *obj, *lowestPriorityObj;
111 tdfxMemRange *r;
112 FxU32 prev;
113
114 printf("DUMP Objects:\n");
115 for (obj = mesaShared->TexObjectList; obj; obj = obj->Next) {
116 tdfxTexInfo *info = TDFX_TEXTURE_DATA(obj);
117
118 if (info && info->isInTM) {
119 printf("Obj %8p: %4d info = %p\n", obj, obj->Name, info);
120
121 printf(" isInTM=%d whichTMU=%d lastTimeUsed=%d\n",
122 info->isInTM, info->whichTMU, info->lastTimeUsed);
123 printf(" tm[0] = %p", info->tm[0]);
124 assert(info->tm[0]);
125 if (info->tm[0]) {
126 printf(" tm startAddr = %d endAddr = %d",
127 info->tm[0]->startAddr,
128 info->tm[0]->endAddr);
129 }
130 printf("\n");
131 printf(" tm[1] = %p", info->tm[1]);
132 if (info->tm[1]) {
133 printf(" tm startAddr = %d endAddr = %d",
134 info->tm[1]->startAddr,
135 info->tm[1]->endAddr);
136 }
137 printf("\n");
138 }
139 }
140
141 VerifyFreeList(fxMesa, 0);
142 VerifyFreeList(fxMesa, 1);
143
144 printf("Free memory unit 0: %d bytes\n", shared->freeTexMem[0]);
145 prev = 0;
146 for (r = shared->tmFree[0]; r; r = r->next) {
147 printf("%8p: start %8d end %8d size %8d gap %8d\n", r, r->startAddr, r->endAddr, r->endAddr - r->startAddr, r->startAddr - prev);
148 prev = r->endAddr;
149 }
150
151 printf("Free memory unit 1: %d bytes\n", shared->freeTexMem[1]);
152 prev = 0;
153 for (r = shared->tmFree[1]; r; r = r->next) {
154 printf("%8p: start %8d end %8d size %8d gap %8d\n", r, r->startAddr, r->endAddr, r->endAddr - r->startAddr, r->startAddr - prev);
155 prev = r->endAddr;
156 }
157
158 }
159 #endif
160
161
162
163 #ifdef TEXSANITY
164 static void
165 fubar(void)
166 {
167 }
168
169 /*
170 * Sanity Check
171 */
172 static void
173 sanity(tdfxContextPtr fxMesa)
174 {
175 tdfxMemRange *tmp, *prev, *pos;
176
177 prev = 0;
178 tmp = fxMesa->tmFree[0];
179 while (tmp) {
180 if (!tmp->startAddr && !tmp->endAddr) {
181 fprintf(stderr, "Textures fubar\n");
182 fubar();
183 }
184 if (tmp->startAddr >= tmp->endAddr) {
185 fprintf(stderr, "Node fubar\n");
186 fubar();
187 }
188 if (prev && (prev->startAddr >= tmp->startAddr ||
189 prev->endAddr > tmp->startAddr)) {
190 fprintf(stderr, "Sorting fubar\n");
191 fubar();
192 }
193 prev = tmp;
194 tmp = tmp->next;
195 }
196 prev = 0;
197 tmp = fxMesa->tmFree[1];
198 while (tmp) {
199 if (!tmp->startAddr && !tmp->endAddr) {
200 fprintf(stderr, "Textures fubar\n");
201 fubar();
202 }
203 if (tmp->startAddr >= tmp->endAddr) {
204 fprintf(stderr, "Node fubar\n");
205 fubar();
206 }
207 if (prev && (prev->startAddr >= tmp->startAddr ||
208 prev->endAddr > tmp->startAddr)) {
209 fprintf(stderr, "Sorting fubar\n");
210 fubar();
211 }
212 prev = tmp;
213 tmp = tmp->next;
214 }
215 }
216 #endif
217
218
219
220
221
222 /*
223 * Allocate and initialize a new MemRange struct.
224 * Try to allocate it from the pool of free MemRange nodes rather than malloc.
225 */
226 static tdfxMemRange *
227 NewRangeNode(tdfxContextPtr fxMesa, FxU32 start, FxU32 end)
228 {
229 struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
230 struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
231 tdfxMemRange *result;
232
233 _glthread_LOCK_MUTEX(mesaShared->Mutex);
234 if (shared && shared->tmPool) {
235 result = shared->tmPool;
236 shared->tmPool = shared->tmPool->next;
237 }
238 else {
239 result = MALLOC(sizeof(tdfxMemRange));
240
241 }
242 _glthread_UNLOCK_MUTEX(mesaShared->Mutex);
243
244 if (!result) {
245 /*fprintf(stderr, "fxDriver: out of memory!\n");*/
246 return NULL;
247 }
248
249 result->startAddr = start;
250 result->endAddr = end;
251 result->next = NULL;
252
253 return result;
254 }
255
256
257 /*
258 * Initialize texture memory.
259 * We take care of one or both TMU's here.
260 */
261 void
262 tdfxTMInit(tdfxContextPtr fxMesa)
263 {
264 if (!fxMesa->glCtx->Shared->DriverData) {
265 const char *extensions;
266 struct tdfxSharedState *shared = CALLOC_STRUCT(tdfxSharedState);
267 if (!shared)
268 return;
269
270 LOCK_HARDWARE(fxMesa);
271 extensions = fxMesa->Glide.grGetString(GR_EXTENSION);
272 UNLOCK_HARDWARE(fxMesa);
273 if (strstr(extensions, "TEXUMA")) {
274 FxU32 start, end;
275 shared->umaTexMemory = GL_TRUE;
276 LOCK_HARDWARE(fxMesa);
277 fxMesa->Glide.grEnable(GR_TEXTURE_UMA_EXT);
278 start = fxMesa->Glide.grTexMinAddress(0);
279 end = fxMesa->Glide.grTexMaxAddress(0);
280 UNLOCK_HARDWARE(fxMesa);
281 shared->totalTexMem[0] = end - start;
282 shared->totalTexMem[1] = 0;
283 shared->freeTexMem[0] = end - start;
284 shared->freeTexMem[1] = 0;
285 shared->tmFree[0] = NewRangeNode(fxMesa, start, end);
286 shared->tmFree[1] = NULL;
287 /*printf("UMA tex memory: %d\n", (int) (end - start));*/
288 }
289 else {
290 const int numTMUs = fxMesa->haveTwoTMUs ? 2 : 1;
291 int tmu;
292 shared->umaTexMemory = GL_FALSE;
293 LOCK_HARDWARE(fxMesa);
294 for (tmu = 0; tmu < numTMUs; tmu++) {
295 FxU32 start = fxMesa->Glide.grTexMinAddress(tmu);
296 FxU32 end = fxMesa->Glide.grTexMaxAddress(tmu);
297 shared->totalTexMem[tmu] = end - start;
298 shared->freeTexMem[tmu] = end - start;
299 shared->tmFree[tmu] = NewRangeNode(fxMesa, start, end);
300 /*printf("Split tex memory: %d\n", (int) (end - start));*/
301 }
302 UNLOCK_HARDWARE(fxMesa);
303 }
304
305 shared->tmPool = NULL;
306 fxMesa->glCtx->Shared->DriverData = shared;
307 /*printf("Texture memory init UMA: %d\n", shared->umaTexMemory);*/
308 }
309 }
310
311
312 /*
313 * Clean-up texture memory before destroying context.
314 */
315 void
316 tdfxTMClose(tdfxContextPtr fxMesa)
317 {
318 if (fxMesa->glCtx->Shared->RefCount == 1 && fxMesa->driDrawable) {
319 /* refcount will soon go to zero, free our 3dfx stuff */
320 struct tdfxSharedState *shared = (struct tdfxSharedState *) fxMesa->glCtx->Shared->DriverData;
321
322 const int numTMUs = fxMesa->haveTwoTMUs ? 2 : 1;
323 int tmu;
324 tdfxMemRange *tmp, *next;
325
326 /* Deallocate the pool of free tdfxMemRange nodes */
327 tmp = shared->tmPool;
328 while (tmp) {
329 next = tmp->next;
330 FREE(tmp);
331 tmp = next;
332 }
333
334 /* Delete the texture memory block tdfxMemRange nodes */
335 for (tmu = 0; tmu < numTMUs; tmu++) {
336 tmp = shared->tmFree[tmu];
337 while (tmp) {
338 next = tmp->next;
339 FREE(tmp);
340 tmp = next;
341 }
342 }
343
344 FREE(shared);
345 fxMesa->glCtx->Shared->DriverData = NULL;
346 }
347 }
348
349
350
351 /*
352 * Delete a tdfxMemRange struct.
353 * We keep a linked list of free/available tdfxMemRange structs to
354 * avoid extra malloc/free calls.
355 */
356 #if 0
357 static void
358 DeleteRangeNode_NoLock(struct TdfxSharedState *shared, tdfxMemRange *range)
359 {
360 /* insert at head of list */
361 range->next = shared->tmPool;
362 shared->tmPool = range;
363 }
364 #endif
365
366 #define DELETE_RANGE_NODE(shared, range) \
367 (range)->next = (shared)->tmPool; \
368 (shared)->tmPool = (range)
369
370
371
372 /*
373 * When we've run out of texture memory we have to throw out an
374 * existing texture to make room for the new one. This function
375 * determins the texture to throw out.
376 */
377 static struct gl_texture_object *
378 FindOldestObject(tdfxContextPtr fxMesa, FxU32 tmu)
379 {
380 const GLuint bindnumber = fxMesa->texBindNumber;
381 struct gl_texture_object *oldestObj, *obj, *lowestPriorityObj;
382 GLfloat lowestPriority;
383 GLuint oldestAge;
384
385 oldestObj = NULL;
386 oldestAge = 0;
387
388 lowestPriority = 1.0F;
389 lowestPriorityObj = NULL;
390
391 for (obj = fxMesa->glCtx->Shared->TexObjectList; obj; obj = obj->Next) {
392 tdfxTexInfo *info = TDFX_TEXTURE_DATA(obj);
393
394 if (info && info->isInTM &&
395 ((info->whichTMU == tmu) || (info->whichTMU == TDFX_TMU_BOTH) ||
396 (info->whichTMU == TDFX_TMU_SPLIT))) {
397 GLuint age, lasttime;
398
399 assert(info->tm[0]);
400 lasttime = info->lastTimeUsed;
401
402 if (lasttime > bindnumber)
403 age = bindnumber + (UINT_MAX - lasttime + 1); /* TO DO: check wrap around */
404 else
405 age = bindnumber - lasttime;
406
407 if (age >= oldestAge) {
408 oldestAge = age;
409 oldestObj = obj;
410 }
411
412 /* examine priority */
413 if (obj->Priority < lowestPriority) {
414 lowestPriority = obj->Priority;
415 lowestPriorityObj = obj;
416 }
417 }
418 }
419
420 if (lowestPriority < 1.0) {
421 ASSERT(lowestPriorityObj);
422 /*
423 printf("discard %d pri=%f\n", lowestPriorityObj->Name, lowestPriority);
424 */
425 return lowestPriorityObj;
426 }
427 else {
428 /*
429 printf("discard %d age=%d\n", oldestObj->Name, oldestAge);
430 */
431 return oldestObj;
432 }
433 }
434
435
436 #if 0
437 static void
438 FlushTexMemory(tdfxContextPtr fxMesa)
439 {
440 struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
441 struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
442 struct gl_texture_object *obj;
443
444 for (obj = mesaShared->TexObjectList; obj; obj = obj->Next) {
445 if (obj->RefCount < 2) {
446 /* don't flush currently bound textures */
447 tdfxTMMoveOutTM_NoLock(fxMesa, obj);
448 }
449 }
450 }
451 #endif
452
453
454 /*
455 * Find the address (offset?) at which we can store a new texture.
456 * <tmu> is the texture unit.
457 * <size> is the texture size in bytes.
458 */
459 static FxU32
460 FindStartAddr(tdfxContextPtr fxMesa, FxU32 tmu, FxU32 size)
461 {
462 struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
463 struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
464 tdfxMemRange *prev, *block;
465 FxU32 result;
466 #if 0
467 int discardedCount = 0;
468 #define MAX_DISCARDS 10
469 #endif
470
471 if (shared->umaTexMemory) {
472 assert(tmu == TDFX_TMU0);
473 }
474
475 _glthread_LOCK_MUTEX(mesaShared->Mutex);
476 while (1) {
477 prev = NULL;
478 block = shared->tmFree[tmu];
479 while (block) {
480 if (block->endAddr - block->startAddr >= size) {
481 /* The texture will fit here */
482 result = block->startAddr;
483 block->startAddr += size;
484 if (block->startAddr == block->endAddr) {
485 /* Remove this node since it's empty */
486 if (prev) {
487 prev->next = block->next;
488 }
489 else {
490 shared->tmFree[tmu] = block->next;
491 }
492 DELETE_RANGE_NODE(shared, block);
493 }
494 shared->freeTexMem[tmu] -= size;
495 _glthread_UNLOCK_MUTEX(mesaShared->Mutex);
496 return result;
497 }
498 prev = block;
499 block = block->next;
500 }
501 /* We failed to find a block large enough to accomodate <size> bytes.
502 * Find the oldest texObject and free it.
503 */
504 #if 0
505 discardedCount++;
506 if (discardedCount > MAX_DISCARDS + 1) {
507 _mesa_problem(NULL, "%s: extreme texmem fragmentation", __FUNCTION__);
508 _glthread_UNLOCK_MUTEX(mesaShared->Mutex);
509 return BAD_ADDRESS;
510 }
511 else if (discardedCount > MAX_DISCARDS) {
512 /* texture memory is probably really fragmented, flush it */
513 FlushTexMemory(fxMesa);
514 }
515 else
516 #endif
517 {
518 struct gl_texture_object *obj = FindOldestObject(fxMesa, tmu);
519 if (obj) {
520 tdfxTMMoveOutTM_NoLock(fxMesa, obj);
521 fxMesa->stats.texSwaps++;
522 }
523 else {
524 _mesa_problem(NULL, "%s: extreme texmem fragmentation", __FUNCTION__);
525 _glthread_UNLOCK_MUTEX(mesaShared->Mutex);
526 return BAD_ADDRESS;
527 }
528 }
529 }
530
531 /* never get here, but play it safe */
532 _glthread_UNLOCK_MUTEX(mesaShared->Mutex);
533 return BAD_ADDRESS;
534 }
535
536
537 /*
538 * Remove the given tdfxMemRange node from hardware texture memory.
539 */
540 static void
541 RemoveRange_NoLock(tdfxContextPtr fxMesa, FxU32 tmu, tdfxMemRange *range)
542 {
543 struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
544 struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
545 tdfxMemRange *block, *prev;
546
547 if (shared->umaTexMemory) {
548 assert(tmu == TDFX_TMU0);
549 }
550
551 if (!range)
552 return;
553
554 if (range->startAddr == range->endAddr) {
555 DELETE_RANGE_NODE(shared, range);
556 return;
557 }
558 shared->freeTexMem[tmu] += range->endAddr - range->startAddr;
559
560 /* find position in linked list to insert this tdfxMemRange node */
561 prev = NULL;
562 block = shared->tmFree[tmu];
563 while (block) {
564 assert(range->startAddr != block->startAddr);
565 if (range->startAddr > block->startAddr) {
566 prev = block;
567 block = block->next;
568 }
569 else {
570 break;
571 }
572 }
573
574 /* Insert the free block, combine with adjacent blocks when possible */
575 range->next = block;
576 if (block) {
577 if (range->endAddr == block->startAddr) {
578 /* Combine */
579 block->startAddr = range->startAddr;
580 DELETE_RANGE_NODE(shared, range);
581 range = block;
582 }
583 }
584 if (prev) {
585 if (prev->endAddr == range->startAddr) {
586 /* Combine */
587 prev->endAddr = range->endAddr;
588 prev->next = range->next;
589 DELETE_RANGE_NODE(shared, range);
590 }
591 else {
592 prev->next = range;
593 }
594 }
595 else {
596 shared->tmFree[tmu] = range;
597 }
598 }
599
600
601 #if 0 /* NOT USED */
602 static void
603 RemoveRange(tdfxContextPtr fxMesa, FxU32 tmu, tdfxMemRange *range)
604 {
605 struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
606 _glthread_LOCK_MUTEX(mesaShared->Mutex);
607 RemoveRange_NoLock(fxMesa, tmu, range);
608 _glthread_UNLOCK_MUTEX(mesaShared->Mutex);
609 }
610 #endif
611
612
613 /*
614 * Allocate space for a texture image.
615 * <tmu> is the texture unit
616 * <texmemsize> is the number of bytes to allocate
617 */
618 static tdfxMemRange *
619 AllocTexMem(tdfxContextPtr fxMesa, FxU32 tmu, FxU32 texmemsize)
620 {
621 FxU32 startAddr;
622 startAddr = FindStartAddr(fxMesa, tmu, texmemsize);
623 if (startAddr == BAD_ADDRESS) {
624 _mesa_problem(fxMesa->glCtx, "%s returned NULL! tmu=%d texmemsize=%d",
625 __FUNCTION__, (int) tmu, (int) texmemsize);
626 return NULL;
627 }
628 else {
629 tdfxMemRange *range;
630 range = NewRangeNode(fxMesa, startAddr, startAddr + texmemsize);
631 return range;
632 }
633 }
634
635
636 /*
637 * Download (copy) the given texture data (all mipmap levels) into the
638 * Voodoo's texture memory.
639 * The texture memory must have already been allocated.
640 */
641 void
642 tdfxTMDownloadTexture(tdfxContextPtr fxMesa, struct gl_texture_object *tObj)
643 {
644 tdfxTexInfo *ti;
645 GLint l;
646 FxU32 targetTMU;
647
648 assert(tObj);
649 ti = TDFX_TEXTURE_DATA(tObj);
650 assert(ti);
651 targetTMU = ti->whichTMU;
652
653 switch (targetTMU) {
654 case TDFX_TMU0:
655 case TDFX_TMU1:
656 if (ti->tm[targetTMU]) {
657 for (l = ti->minLevel; l <= ti->maxLevel
658 && tObj->Image[l]->Data; l++) {
659 GrLOD_t glideLod = ti->info.largeLodLog2 - l + tObj->BaseLevel;
660 fxMesa->Glide.grTexDownloadMipMapLevel(targetTMU,
661 ti->tm[targetTMU]->startAddr,
662 glideLod,
663 ti->info.largeLodLog2,
664 ti->info.aspectRatioLog2,
665 ti->info.format,
666 GR_MIPMAPLEVELMASK_BOTH,
667 tObj->Image[l]->Data);
668 }
669 }
670 break;
671 case TDFX_TMU_SPLIT:
672 if (ti->tm[TDFX_TMU0] && ti->tm[TDFX_TMU1]) {
673 for (l = ti->minLevel; l <= ti->maxLevel
674 && tObj->Image[l]->Data; l++) {
675 GrLOD_t glideLod = ti->info.largeLodLog2 - l + tObj->BaseLevel;
676 fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU0,
677 ti->tm[TDFX_TMU0]->startAddr,
678 glideLod,
679 ti->info.largeLodLog2,
680 ti->info.aspectRatioLog2,
681 ti->info.format,
682 GR_MIPMAPLEVELMASK_ODD,
683 tObj->Image[l]->Data);
684
685 fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU1,
686 ti->tm[TDFX_TMU1]->startAddr,
687 glideLod,
688 ti->info.largeLodLog2,
689 ti->info.aspectRatioLog2,
690 ti->info.format,
691 GR_MIPMAPLEVELMASK_EVEN,
692 tObj->Image[l]->Data);
693 }
694 }
695 break;
696 case TDFX_TMU_BOTH:
697 if (ti->tm[TDFX_TMU0] && ti->tm[TDFX_TMU1]) {
698 for (l = ti->minLevel; l <= ti->maxLevel
699 && tObj->Image[l]->Data; l++) {
700 GrLOD_t glideLod = ti->info.largeLodLog2 - l + tObj->BaseLevel;
701 fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU0,
702 ti->tm[TDFX_TMU0]->startAddr,
703 glideLod,
704 ti->info.largeLodLog2,
705 ti->info.aspectRatioLog2,
706 ti->info.format,
707 GR_MIPMAPLEVELMASK_BOTH,
708 tObj->Image[l]->Data);
709
710 fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU1,
711 ti->tm[TDFX_TMU1]->startAddr,
712 glideLod,
713 ti->info.largeLodLog2,
714 ti->info.aspectRatioLog2,
715 ti->info.format,
716 GR_MIPMAPLEVELMASK_BOTH,
717 tObj->Image[l]->Data);
718 }
719 }
720 break;
721 default:
722 _mesa_problem(NULL, "%s: bad tmu (%d)", __FUNCTION__, (int)targetTMU);
723 return;
724 }
725 }
726
727
728 void
729 tdfxTMReloadMipMapLevel(GLcontext *ctx, struct gl_texture_object *tObj,
730 GLint level)
731 {
732 tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx);
733 tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj);
734 GrLOD_t glideLod;
735 FxU32 tmu;
736
737 tmu = ti->whichTMU;
738 glideLod = ti->info.largeLodLog2 - level + tObj->BaseLevel;
739 ASSERT(ti->isInTM);
740
741 LOCK_HARDWARE(fxMesa);
742
743 switch (tmu) {
744 case TDFX_TMU0:
745 case TDFX_TMU1:
746 fxMesa->Glide.grTexDownloadMipMapLevel(tmu,
747 ti->tm[tmu]->startAddr,
748 glideLod,
749 ti->info.largeLodLog2,
750 ti->info.aspectRatioLog2,
751 ti->info.format,
752 GR_MIPMAPLEVELMASK_BOTH,
753 tObj->Image[level]->Data);
754 break;
755 case TDFX_TMU_SPLIT:
756 fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU0,
757 ti->tm[GR_TMU0]->startAddr,
758 glideLod,
759 ti->info.largeLodLog2,
760 ti->info.aspectRatioLog2,
761 ti->info.format,
762 GR_MIPMAPLEVELMASK_ODD,
763 tObj->Image[level]->Data);
764
765 fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU1,
766 ti->tm[GR_TMU1]->startAddr,
767 glideLod,
768 ti->info.largeLodLog2,
769 ti->info.aspectRatioLog2,
770 ti->info.format,
771 GR_MIPMAPLEVELMASK_EVEN,
772 tObj->Image[level]->Data);
773 break;
774 case TDFX_TMU_BOTH:
775 fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU0,
776 ti->tm[GR_TMU0]->startAddr,
777 glideLod,
778 ti->info.largeLodLog2,
779 ti->info.aspectRatioLog2,
780 ti->info.format,
781 GR_MIPMAPLEVELMASK_BOTH,
782 tObj->Image[level]->Data);
783
784 fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU1,
785 ti->tm[GR_TMU1]->startAddr,
786 glideLod,
787 ti->info.largeLodLog2,
788 ti->info.aspectRatioLog2,
789 ti->info.format,
790 GR_MIPMAPLEVELMASK_BOTH,
791 tObj->Image[level]->Data);
792 break;
793
794 default:
795 _mesa_problem(ctx, "%s: bad tmu (%d)", __FUNCTION__, (int)tmu);
796 break;
797 }
798 UNLOCK_HARDWARE(fxMesa);
799 }
800
801
802 /*
803 * Allocate space for the given texture in texture memory then
804 * download (copy) it into that space.
805 */
806 void
807 tdfxTMMoveInTM_NoLock( tdfxContextPtr fxMesa, struct gl_texture_object *tObj,
808 FxU32 targetTMU )
809 {
810 tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj);
811 FxU32 texmemsize;
812
813 fxMesa->stats.reqTexUpload++;
814
815 if (ti->isInTM) {
816 if (ti->whichTMU == targetTMU)
817 return;
818 if (targetTMU == TDFX_TMU_SPLIT || ti->whichTMU == TDFX_TMU_SPLIT) {
819 tdfxTMMoveOutTM_NoLock(fxMesa, tObj);
820 }
821 else {
822 if (ti->whichTMU == TDFX_TMU_BOTH)
823 return;
824 targetTMU = TDFX_TMU_BOTH;
825 }
826 }
827
828 ti->whichTMU = targetTMU;
829
830 switch (targetTMU) {
831 case TDFX_TMU0:
832 case TDFX_TMU1:
833 texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH,
834 &(ti->info));
835 ti->tm[targetTMU] = AllocTexMem(fxMesa, targetTMU, texmemsize);
836 break;
837 case TDFX_TMU_SPLIT:
838 texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_ODD,
839 &(ti->info));
840 ti->tm[TDFX_TMU0] = AllocTexMem(fxMesa, TDFX_TMU0, texmemsize);
841 if (ti->tm[TDFX_TMU0])
842 fxMesa->stats.memTexUpload += texmemsize;
843
844 texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_EVEN,
845 &(ti->info));
846 ti->tm[TDFX_TMU1] = AllocTexMem(fxMesa, TDFX_TMU1, texmemsize);
847 break;
848 case TDFX_TMU_BOTH:
849 texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH,
850 &(ti->info));
851 ti->tm[TDFX_TMU0] = AllocTexMem(fxMesa, TDFX_TMU0, texmemsize);
852 if (ti->tm[TDFX_TMU0])
853 fxMesa->stats.memTexUpload += texmemsize;
854
855 texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH,
856 &(ti->info));
857 ti->tm[TDFX_TMU1] = AllocTexMem(fxMesa, TDFX_TMU1, texmemsize);
858 break;
859 default:
860 _mesa_problem(NULL, "%s: bad tmu (%d)", __FUNCTION__, (int)targetTMU);
861 return;
862 }
863
864 ti->reloadImages = GL_TRUE;
865 ti->isInTM = GL_TRUE;
866
867 fxMesa->stats.texUpload++;
868 }
869
870
871 /*
872 * Move the given texture out of hardware texture memory.
873 * This deallocates the texture's memory space.
874 */
875 void
876 tdfxTMMoveOutTM_NoLock( tdfxContextPtr fxMesa, struct gl_texture_object *tObj )
877 {
878 struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared;
879 struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData;
880 tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj);
881
882 if (MESA_VERBOSE & VERBOSE_DRIVER) {
883 fprintf(stderr, "fxmesa: %s(%p (%d))\n", __FUNCTION__, tObj, tObj->Name);
884 }
885
886 /*
887 VerifyFreeList(fxMesa, 0);
888 VerifyFreeList(fxMesa, 1);
889 */
890
891 if (!ti || !ti->isInTM)
892 return;
893
894 switch (ti->whichTMU) {
895 case TDFX_TMU0:
896 case TDFX_TMU1:
897 RemoveRange_NoLock(fxMesa, ti->whichTMU, ti->tm[ti->whichTMU]);
898 break;
899 case TDFX_TMU_SPLIT:
900 case TDFX_TMU_BOTH:
901 assert(!shared->umaTexMemory);
902 RemoveRange_NoLock(fxMesa, TDFX_TMU0, ti->tm[TDFX_TMU0]);
903 RemoveRange_NoLock(fxMesa, TDFX_TMU1, ti->tm[TDFX_TMU1]);
904 break;
905 default:
906 _mesa_problem(NULL, "%s: bad tmu (%d)", __FUNCTION__, (int)ti->whichTMU);
907 return;
908 }
909
910 ti->isInTM = GL_FALSE;
911 ti->tm[0] = NULL;
912 ti->tm[1] = NULL;
913 ti->whichTMU = TDFX_TMU_NONE;
914
915 /*
916 VerifyFreeList(fxMesa, 0);
917 VerifyFreeList(fxMesa, 1);
918 */
919 }
920
921
922 /*
923 * Called via glDeleteTexture to delete a texture object.
924 */
925 void
926 tdfxTMFreeTexture(tdfxContextPtr fxMesa, struct gl_texture_object *tObj)
927 {
928 tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj);
929 if (ti) {
930 tdfxTMMoveOutTM(fxMesa, tObj);
931 FREE(ti);
932 tObj->DriverData = NULL;
933 }
934 /*
935 VerifyFreeList(fxMesa, 0);
936 VerifyFreeList(fxMesa, 1);
937 */
938 }
939
940
941
942 /*
943 * After a context switch this function will be called to restore
944 * texture memory for the new context.
945 */
946 void tdfxTMRestoreTextures_NoLock( tdfxContextPtr fxMesa )
947 {
948 GLcontext *ctx = fxMesa->glCtx;
949 struct gl_texture_object *tObj;
950 int i;
951
952 for ( tObj = ctx->Shared->TexObjectList ; tObj ; tObj = tObj->Next ) {
953 tdfxTexInfo *ti = TDFX_TEXTURE_DATA( tObj );
954 if ( ti && ti->isInTM ) {
955 for ( i = 0 ; i < MAX_TEXTURE_UNITS ; i++ ) {
956 if ( ctx->Texture.Unit[i]._Current == tObj ) {
957 tdfxTMDownloadTexture( fxMesa, tObj );
958 break;
959 }
960 }
961 if ( i == MAX_TEXTURE_UNITS ) {
962 tdfxTMMoveOutTM_NoLock( fxMesa, tObj );
963 }
964 }
965 }
966 /*
967 VerifyFreeList(fxMesa, 0);
968 VerifyFreeList(fxMesa, 1);
969 */
970 }