dri/radeon: export a function to cleanup a texture object.
[mesa.git] / src / mesa / drivers / dri / common / texmem.c
1 /*
2 * Copyright 2000-2001 VA Linux Systems, Inc.
3 * (C) Copyright IBM Corporation 2002, 2003
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * on the rights to use, copy, modify, merge, publish, distribute, sub
10 * license, and/or sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20 * VA LINUX SYSTEM, IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23 * USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 * Ian Romanick <idr@us.ibm.com>
27 * Keith Whitwell <keithw@tungstengraphics.com>
28 * Kevin E. Martin <kem@users.sourceforge.net>
29 * Gareth Hughes <gareth@nvidia.com>
30 */
31
32 /** \file texmem.c
33 * Implements all of the device-independent texture memory management.
34 *
35 * Currently, only a simple LRU texture memory management policy is
36 * implemented. In the (hopefully very near) future, better policies will be
37 * implemented. The idea is that the DRI should be able to run in one of two
38 * modes. In the default mode the DRI will dynamically attempt to discover
39 * the best texture management policy for the running application. In the
40 * other mode, the user (via some sort of as yet TBD mechanism) will select
41 * a texture management policy that is known to work well with the
42 * application.
43 */
44
45 #include "texmem.h"
46 #include "main/simple_list.h"
47 #include "main/imports.h"
48 #include "main/macros.h"
49 #include "main/texformat.h"
50
51 #include <assert.h>
52
53
54
55 static unsigned dummy_swap_counter;
56
57
58 /**
59 * Calculate \f$\log_2\f$ of a value. This is a particularly poor
60 * implementation of this function. However, since system performance is in
61 * no way dependent on this function, the slowness of the implementation is
62 * irrelevent.
63 *
64 * \param n Value whose \f$\log_2\f$ is to be calculated
65 */
66
67 static GLuint
68 driLog2( GLuint n )
69 {
70 GLuint log2;
71
72 for ( log2 = 1 ; n > 1 ; log2++ ) {
73 n >>= 1;
74 }
75
76 return log2;
77 }
78
79
80
81
82 /**
83 * Determine if a texture is resident in textureable memory. Depending on
84 * the driver, this may or may not be on-card memory. It could be AGP memory
85 * or anyother type of memory from which the hardware can directly read
86 * texels.
87 *
88 * This function is intended to be used as the \c IsTextureResident function
89 * in the device's \c dd_function_table.
90 *
91 * \param ctx GL context pointer (currently unused)
92 * \param texObj Texture object to be tested
93 */
94
95 GLboolean
96 driIsTextureResident( GLcontext * ctx,
97 struct gl_texture_object * texObj )
98 {
99 driTextureObject * t;
100
101
102 t = (driTextureObject *) texObj->DriverData;
103 return( (t != NULL) && (t->memBlock != NULL) );
104 }
105
106
107
108
109 /**
110 * (Re)initialize the global circular LRU list. The last element
111 * in the array (\a heap->nrRegions) is the sentinal. Keeping it
112 * at the end of the array allows the other elements of the array
113 * to be addressed rationally when looking up objects at a particular
114 * location in texture memory.
115 *
116 * \param heap Texture heap to be reset
117 */
118
119 static void resetGlobalLRU( driTexHeap * heap )
120 {
121 drmTextureRegionPtr list = heap->global_regions;
122 unsigned sz = 1U << heap->logGranularity;
123 unsigned i;
124
125 for (i = 0 ; (i+1) * sz <= heap->size ; i++) {
126 list[i].prev = i-1;
127 list[i].next = i+1;
128 list[i].age = 0;
129 }
130
131 i--;
132 list[0].prev = heap->nrRegions;
133 list[i].prev = i-1;
134 list[i].next = heap->nrRegions;
135 list[heap->nrRegions].prev = i;
136 list[heap->nrRegions].next = 0;
137 heap->global_age[0] = 0;
138 }
139
140 /**
141 * Print out debugging information about the local texture LRU.
142 *
143 * \param heap Texture heap to be printed
144 * \param callername Name of calling function
145 */
146 static void printLocalLRU( driTexHeap * heap, const char *callername )
147 {
148 driTextureObject *t;
149 unsigned sz = 1U << heap->logGranularity;
150
151 fprintf( stderr, "%s in %s:\nLocal LRU, heap %d:\n",
152 __FUNCTION__, callername, heap->heapId );
153
154 foreach ( t, &heap->texture_objects ) {
155 if (!t->memBlock)
156 continue;
157 if (!t->tObj) {
158 fprintf( stderr, "Placeholder (%p) %d at 0x%x sz 0x%x\n",
159 (void *)t,
160 t->memBlock->ofs / sz,
161 t->memBlock->ofs,
162 t->memBlock->size );
163 } else {
164 fprintf( stderr, "Texture (%p) at 0x%x sz 0x%x\n",
165 (void *)t,
166 t->memBlock->ofs,
167 t->memBlock->size );
168 }
169 }
170 foreach ( t, heap->swapped_objects ) {
171 if (!t->tObj) {
172 fprintf( stderr, "Swapped Placeholder (%p)\n", (void *)t );
173 } else {
174 fprintf( stderr, "Swapped Texture (%p)\n", (void *)t );
175 }
176 }
177
178 fprintf( stderr, "\n" );
179 }
180
181 /**
182 * Print out debugging information about the global texture LRU.
183 *
184 * \param heap Texture heap to be printed
185 * \param callername Name of calling function
186 */
187 static void printGlobalLRU( driTexHeap * heap, const char *callername )
188 {
189 drmTextureRegionPtr list = heap->global_regions;
190 unsigned int i, j;
191
192 fprintf( stderr, "%s in %s:\nGlobal LRU, heap %d list %p:\n",
193 __FUNCTION__, callername, heap->heapId, (void *)list );
194
195 for ( i = 0, j = heap->nrRegions ; i < heap->nrRegions ; i++ ) {
196 fprintf( stderr, "list[%d] age %d next %d prev %d in_use %d\n",
197 j, list[j].age, list[j].next, list[j].prev, list[j].in_use );
198 j = list[j].next;
199 if ( j == heap->nrRegions ) break;
200 }
201
202 if ( j != heap->nrRegions ) {
203 fprintf( stderr, "Loop detected in global LRU\n" );
204 for ( i = 0 ; i < heap->nrRegions ; i++ ) {
205 fprintf( stderr, "list[%d] age %d next %d prev %d in_use %d\n",
206 i, list[i].age, list[i].next, list[i].prev, list[i].in_use );
207 }
208 }
209
210 fprintf( stderr, "\n" );
211 }
212
213
214 /**
215 * Called by the client whenever it touches a local texture.
216 *
217 * \param t Texture object that the client has accessed
218 */
219
220 void driUpdateTextureLRU( driTextureObject * t )
221 {
222 driTexHeap * heap;
223 drmTextureRegionPtr list;
224 unsigned shift;
225 unsigned start;
226 unsigned end;
227 unsigned i;
228
229
230 heap = t->heap;
231 if ( heap != NULL ) {
232 shift = heap->logGranularity;
233 start = t->memBlock->ofs >> shift;
234 end = (t->memBlock->ofs + t->memBlock->size - 1) >> shift;
235
236
237 heap->local_age = ++heap->global_age[0];
238 list = heap->global_regions;
239
240
241 /* Update the context's local LRU
242 */
243
244 move_to_head( & heap->texture_objects, t );
245
246
247 for (i = start ; i <= end ; i++) {
248 list[i].age = heap->local_age;
249
250 /* remove_from_list(i)
251 */
252 list[(unsigned)list[i].next].prev = list[i].prev;
253 list[(unsigned)list[i].prev].next = list[i].next;
254
255 /* insert_at_head(list, i)
256 */
257 list[i].prev = heap->nrRegions;
258 list[i].next = list[heap->nrRegions].next;
259 list[(unsigned)list[heap->nrRegions].next].prev = i;
260 list[heap->nrRegions].next = i;
261 }
262
263 if ( 0 ) {
264 printGlobalLRU( heap, __FUNCTION__ );
265 printLocalLRU( heap, __FUNCTION__ );
266 }
267 }
268 }
269
270
271
272
273 /**
274 * Keep track of swapped out texture objects.
275 *
276 * \param t Texture object to be "swapped" out of its texture heap
277 */
278
279 void driSwapOutTextureObject( driTextureObject * t )
280 {
281 unsigned face;
282
283
284 if ( t->memBlock != NULL ) {
285 assert( t->heap != NULL );
286 mmFreeMem( t->memBlock );
287 t->memBlock = NULL;
288
289 if (t->timestamp > t->heap->timestamp)
290 t->heap->timestamp = t->timestamp;
291
292 t->heap->texture_swaps[0]++;
293 move_to_tail( t->heap->swapped_objects, t );
294 t->heap = NULL;
295 }
296 else {
297 assert( t->heap == NULL );
298 }
299
300
301 for ( face = 0 ; face < 6 ; face++ ) {
302 t->dirty_images[face] = ~0;
303 }
304 }
305
306
307
308
309 /**
310 * Destroy hardware state associated with texture \a t. Calls the
311 * \a destroy_texture_object method associated with the heap from which
312 * \a t was allocated.
313 *
314 * \param t Texture object to be destroyed
315 */
316
317 void driCleanupTextureObject( driTextureObject * t )
318 {
319 driTexHeap * heap;
320
321 if ( 0 ) {
322 fprintf( stderr, "[%s:%d] freeing %p (tObj = %p, DriverData = %p)\n",
323 __FILE__, __LINE__,
324 (void *)t,
325 (void *)((t != NULL) ? t->tObj : NULL),
326 (void *)((t != NULL && t->tObj != NULL) ? t->tObj->DriverData : NULL ));
327 }
328
329 if ( t != NULL ) {
330 if ( t->memBlock ) {
331 heap = t->heap;
332 assert( heap != NULL );
333
334 heap->texture_swaps[0]++;
335
336 mmFreeMem( t->memBlock );
337 t->memBlock = NULL;
338
339 if (t->timestamp > t->heap->timestamp)
340 t->heap->timestamp = t->timestamp;
341
342 heap->destroy_texture_object( heap->driverContext, t );
343 t->heap = NULL;
344 }
345
346 if ( t->tObj != NULL ) {
347 assert( t->tObj->DriverData == t );
348 t->tObj->DriverData = NULL;
349 }
350
351 remove_from_list( t );
352 }
353
354 if ( 0 ) {
355 fprintf( stderr, "[%s:%d] done freeing %p\n", __FILE__, __LINE__, (void *)t );
356 }
357 }
358
359
360 void driDestroyTextureObject( driTextureObject * t )
361 {
362 driTexHeap * heap;
363
364 if (t == NULL)
365 return;
366
367 driCleanupTextureObject(t);
368 FREE(t);
369 }
370
371
372
373
374 /**
375 * Update the local heap's representation of texture memory based on
376 * data in the SAREA. This is done each time it is detected that some other
377 * direct rendering client has held the lock. This pertains to both our local
378 * textures and the textures belonging to other clients. Keep track of other
379 * client's textures by pushing a placeholder texture onto the LRU list --
380 * these are denoted by \a tObj being \a NULL.
381 *
382 * \param heap Heap whose state is to be updated
383 * \param offset Byte offset in the heap that has been stolen
384 * \param size Size, in bytes, of the stolen block
385 * \param in_use Non-zero if the block is pinned/reserved by the kernel
386 */
387
388 static void driTexturesGone( driTexHeap * heap, int offset, int size,
389 int in_use )
390 {
391 driTextureObject * t;
392 driTextureObject * tmp;
393
394
395 foreach_s ( t, tmp, & heap->texture_objects ) {
396 if ( (t->memBlock->ofs < (offset + size))
397 && ((t->memBlock->ofs + t->memBlock->size) > offset) ) {
398 /* It overlaps - kick it out. If the texture object is just a
399 * place holder, then destroy it all together. Otherwise, mark
400 * it as being swapped out.
401 */
402
403 if ( t->tObj != NULL ) {
404 driSwapOutTextureObject( t );
405 }
406 else {
407 driDestroyTextureObject( t );
408 }
409 }
410 }
411
412
413 {
414 t = (driTextureObject *) CALLOC( heap->texture_object_size );
415 if ( t == NULL ) return;
416
417 t->memBlock = mmAllocMem( heap->memory_heap, size, 0, offset );
418 if ( t->memBlock == NULL ) {
419 fprintf( stderr, "Couldn't alloc placeholder: heap %u sz %x ofs %x\n", heap->heapId,
420 (int)size, (int)offset );
421 mmDumpMemInfo( heap->memory_heap );
422 FREE(t);
423 return;
424 }
425 t->heap = heap;
426 if (in_use)
427 t->reserved = 1;
428 insert_at_head( & heap->texture_objects, t );
429 }
430 }
431
432
433
434
435 /**
436 * Called by the client on lock contention to determine whether textures have
437 * been stolen. If another client has modified a region in which we have
438 * textures, then we need to figure out which of our textures have been
439 * removed and update our global LRU.
440 *
441 * \param heap Texture heap to be updated
442 */
443
444 void driAgeTextures( driTexHeap * heap )
445 {
446 drmTextureRegionPtr list = heap->global_regions;
447 unsigned sz = 1U << (heap->logGranularity);
448 unsigned i, nr = 0;
449
450
451 /* Have to go right round from the back to ensure stuff ends up
452 * LRU in the local list... Fix with a cursor pointer.
453 */
454
455 for (i = list[heap->nrRegions].prev ;
456 i != heap->nrRegions && nr < heap->nrRegions ;
457 i = list[i].prev, nr++) {
458 /* If switching texturing schemes, then the SAREA might not have been
459 * properly cleared, so we need to reset the global texture LRU.
460 */
461
462 if ( (i * sz) > heap->size ) {
463 nr = heap->nrRegions;
464 break;
465 }
466
467 if (list[i].age > heap->local_age)
468 driTexturesGone( heap, i * sz, sz, list[i].in_use);
469 }
470
471 /* Loop or uninitialized heap detected. Reset.
472 */
473
474 if (nr == heap->nrRegions) {
475 driTexturesGone( heap, 0, heap->size, 0);
476 resetGlobalLRU( heap );
477 }
478
479 if ( 0 ) {
480 printGlobalLRU( heap, __FUNCTION__ );
481 printLocalLRU( heap, __FUNCTION__ );
482 }
483
484 heap->local_age = heap->global_age[0];
485 }
486
487
488
489
490 #define INDEX_ARRAY_SIZE 6 /* I'm not aware of driver with more than 2 heaps */
491
492 /**
493 * Allocate memory from a texture heap to hold a texture object. This
494 * routine will attempt to allocate memory for the texture from the heaps
495 * specified by \c heap_array in order. That is, first it will try to
496 * allocate from \c heap_array[0], then \c heap_array[1], and so on.
497 *
498 * \param heap_array Array of pointers to texture heaps to use
499 * \param nr_heaps Number of heap pointer in \a heap_array
500 * \param t Texture object for which space is needed
501 * \return The ID of the heap from which memory was allocated, or -1 if
502 * memory could not be allocated.
503 *
504 * \bug The replacement policy implemented by this function is horrible.
505 */
506
507
508 int
509 driAllocateTexture( driTexHeap * const * heap_array, unsigned nr_heaps,
510 driTextureObject * t )
511 {
512 driTexHeap * heap;
513 driTextureObject * temp;
514 driTextureObject * cursor;
515 unsigned id;
516
517
518 /* In case it already has texture space, initialize heap. This also
519 * prevents GCC from issuing a warning that heap might be used
520 * uninitialized.
521 */
522
523 heap = t->heap;
524
525
526 /* Run through each of the existing heaps and try to allocate a buffer
527 * to hold the texture.
528 */
529
530 for ( id = 0 ; (t->memBlock == NULL) && (id < nr_heaps) ; id++ ) {
531 heap = heap_array[ id ];
532 if ( heap != NULL ) {
533 t->memBlock = mmAllocMem( heap->memory_heap, t->totalSize,
534 heap->alignmentShift, 0 );
535 }
536 }
537
538
539 /* Kick textures out until the requested texture fits.
540 */
541
542 if ( t->memBlock == NULL ) {
543 unsigned index[INDEX_ARRAY_SIZE];
544 unsigned nrGoodHeaps = 0;
545
546 /* Trying to avoid dynamic memory allocation. If you have more
547 * heaps, increase INDEX_ARRAY_SIZE. I'm not aware of any
548 * drivers with more than 2 tex heaps. */
549 assert( nr_heaps < INDEX_ARRAY_SIZE );
550
551 /* Sort large enough heaps by duty. Insertion sort should be
552 * fast enough for such a short array. */
553 for ( id = 0 ; id < nr_heaps ; id++ ) {
554 heap = heap_array[ id ];
555
556 if ( heap != NULL && t->totalSize <= heap->size ) {
557 unsigned j;
558
559 for ( j = 0 ; j < nrGoodHeaps; j++ ) {
560 if ( heap->duty > heap_array[ index[ j ] ]->duty )
561 break;
562 }
563
564 if ( j < nrGoodHeaps ) {
565 memmove( &index[ j+1 ], &index[ j ],
566 sizeof(index[ 0 ]) * (nrGoodHeaps - j) );
567 }
568
569 index[ j ] = id;
570
571 nrGoodHeaps++;
572 }
573 }
574
575 for ( id = 0 ; (t->memBlock == NULL) && (id < nrGoodHeaps) ; id++ ) {
576 heap = heap_array[ index[ id ] ];
577
578 for ( cursor = heap->texture_objects.prev, temp = cursor->prev;
579 cursor != &heap->texture_objects ;
580 cursor = temp, temp = cursor->prev ) {
581
582 /* The the LRU element. If the texture is bound to one of
583 * the texture units, then we cannot kick it out.
584 */
585 if ( cursor->bound || cursor->reserved ) {
586 continue;
587 }
588
589 if ( cursor->memBlock )
590 heap->duty -= cursor->memBlock->size;
591
592 /* If this is a placeholder, there's no need to keep it */
593 if (cursor->tObj)
594 driSwapOutTextureObject( cursor );
595 else
596 driDestroyTextureObject( cursor );
597
598 t->memBlock = mmAllocMem( heap->memory_heap, t->totalSize,
599 heap->alignmentShift, 0 );
600
601 if (t->memBlock)
602 break;
603 }
604 }
605
606 /* Rebalance duties. If a heap kicked more data than its duty,
607 * then all other heaps get that amount multiplied with their
608 * relative weight added to their duty. The negative duty is
609 * reset to 0. In the end all heaps have a duty >= 0.
610 *
611 * CAUTION: we must not change the heap pointer here, because it
612 * is used below to update the texture object.
613 */
614 for ( id = 0 ; id < nr_heaps ; id++ )
615 if ( heap_array[ id ] != NULL && heap_array[ id ]->duty < 0) {
616 int duty = -heap_array[ id ]->duty;
617 double weight = heap_array[ id ]->weight;
618 unsigned j;
619
620 for ( j = 0 ; j < nr_heaps ; j++ )
621 if ( j != id && heap_array[ j ] != NULL ) {
622 heap_array[ j ]->duty += (double) duty *
623 heap_array[ j ]->weight / weight;
624 }
625
626 heap_array[ id ]->duty = 0;
627 }
628 }
629
630
631 if ( t->memBlock != NULL ) {
632 /* id and heap->heapId may or may not be the same value here.
633 */
634
635 assert( heap != NULL );
636 assert( (t->heap == NULL) || (t->heap == heap) );
637
638 t->heap = heap;
639 return heap->heapId;
640 }
641 else {
642 assert( t->heap == NULL );
643
644 fprintf( stderr, "[%s:%d] unable to allocate texture\n",
645 __FUNCTION__, __LINE__ );
646 return -1;
647 }
648 }
649
650
651
652
653
654
655 /**
656 * Set the location where the texture-swap counter is stored.
657 */
658
659 void
660 driSetTextureSwapCounterLocation( driTexHeap * heap, unsigned * counter )
661 {
662 heap->texture_swaps = (counter == NULL) ? & dummy_swap_counter : counter;
663 }
664
665
666
667
668 /**
669 * Create a new heap for texture data.
670 *
671 * \param heap_id Device-dependent heap identifier. This value
672 * will returned by driAllocateTexture when memory
673 * is allocated from this heap.
674 * \param context Device-dependent driver context. This is
675 * supplied as the first parameter to the
676 * \c destroy_tex_obj function.
677 * \param size Size, in bytes, of the texture region
678 * \param alignmentShift Alignment requirement for textures. If textures
679 * must be allocated on a 4096 byte boundry, this
680 * would be 12.
681 * \param nr_regions Number of regions into which this texture space
682 * should be partitioned
683 * \param global_regions Array of \c drmTextureRegion structures in the SAREA
684 * \param global_age Pointer to the global texture age in the SAREA
685 * \param swapped_objects Pointer to the list of texture objects that are
686 * not in texture memory (i.e., have been swapped
687 * out).
688 * \param texture_object_size Size, in bytes, of a device-dependent texture
689 * object
690 * \param destroy_tex_obj Function used to destroy a device-dependent
691 * texture object
692 *
693 * \sa driDestroyTextureHeap
694 */
695
696 driTexHeap *
697 driCreateTextureHeap( unsigned heap_id, void * context, unsigned size,
698 unsigned alignmentShift, unsigned nr_regions,
699 drmTextureRegionPtr global_regions, unsigned * global_age,
700 driTextureObject * swapped_objects,
701 unsigned texture_object_size,
702 destroy_texture_object_t * destroy_tex_obj
703 )
704 {
705 driTexHeap * heap;
706 unsigned l;
707
708
709 if ( 0 )
710 fprintf( stderr, "%s( %u, %p, %u, %u, %u )\n",
711 __FUNCTION__,
712 heap_id, (void *)context, size, alignmentShift, nr_regions );
713
714 heap = (driTexHeap *) CALLOC( sizeof( driTexHeap ) );
715 if ( heap != NULL ) {
716 l = driLog2( (size - 1) / nr_regions );
717 if ( l < alignmentShift )
718 {
719 l = alignmentShift;
720 }
721
722 heap->logGranularity = l;
723 heap->size = size & ~((1L << l) - 1);
724
725 heap->memory_heap = mmInit( 0, heap->size );
726 if ( heap->memory_heap != NULL ) {
727 heap->heapId = heap_id;
728 heap->driverContext = context;
729
730 heap->alignmentShift = alignmentShift;
731 heap->nrRegions = nr_regions;
732 heap->global_regions = global_regions;
733 heap->global_age = global_age;
734 heap->swapped_objects = swapped_objects;
735 heap->texture_object_size = texture_object_size;
736 heap->destroy_texture_object = destroy_tex_obj;
737
738 /* Force global heap init */
739 if (heap->global_age[0] == 0)
740 heap->local_age = ~0;
741 else
742 heap->local_age = 0;
743
744 make_empty_list( & heap->texture_objects );
745 driSetTextureSwapCounterLocation( heap, NULL );
746
747 heap->weight = heap->size;
748 heap->duty = 0;
749 }
750 else {
751 FREE( heap );
752 heap = NULL;
753 }
754 }
755
756
757 if ( 0 )
758 fprintf( stderr, "%s returning %p\n", __FUNCTION__, (void *)heap );
759
760 return heap;
761 }
762
763
764
765
766 /** Destroys a texture heap
767 *
768 * \param heap Texture heap to be destroyed
769 */
770
771 void
772 driDestroyTextureHeap( driTexHeap * heap )
773 {
774 driTextureObject * t;
775 driTextureObject * temp;
776
777
778 if ( heap != NULL ) {
779 foreach_s( t, temp, & heap->texture_objects ) {
780 driDestroyTextureObject( t );
781 }
782 foreach_s( t, temp, heap->swapped_objects ) {
783 driDestroyTextureObject( t );
784 }
785
786 mmDestroy( heap->memory_heap );
787 FREE( heap );
788 }
789 }
790
791
792
793
794 /****************************************************************************/
795 /**
796 * Determine how many texels (including all mipmap levels) would be required
797 * for a texture map of size \f$2^^\c base_size_log2\f$ would require.
798 *
799 * \param base_size_log2 \f$log_2\f$ of the size of a side of the texture
800 * \param dimensions Number of dimensions of the texture. Either 2 or 3.
801 * \param faces Number of faces of the texture. Either 1 or 6 (for cube maps).
802 * \return Number of texels
803 */
804
805 static unsigned
806 texels_this_map_size( int base_size_log2, unsigned dimensions, unsigned faces )
807 {
808 unsigned texels;
809
810
811 assert( (faces == 1) || (faces == 6) );
812 assert( (dimensions == 2) || (dimensions == 3) );
813
814 texels = 0;
815 if ( base_size_log2 >= 0 ) {
816 texels = (1U << (dimensions * base_size_log2));
817
818 /* See http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg03636.html
819 * for the complete explaination of why this formulation is used.
820 * Basically, the smaller mipmap levels sum to 0.333 the size of the
821 * level 0 map. The total size is therefore the size of the map
822 * multipled by 1.333. The +2 is there to round up.
823 */
824
825 texels = (texels * 4 * faces + 2) / 3;
826 }
827
828 return texels;
829 }
830
831
832
833
834 struct maps_per_heap {
835 unsigned c[32];
836 };
837
838 static void
839 fill_in_maximums( driTexHeap * const * heaps, unsigned nr_heaps,
840 unsigned max_bytes_per_texel, unsigned max_size,
841 unsigned mipmaps_at_once, unsigned dimensions,
842 unsigned faces, struct maps_per_heap * max_textures )
843 {
844 unsigned heap;
845 unsigned log2_size;
846 unsigned mask;
847
848
849 /* Determine how many textures of each size can be stored in each
850 * texture heap.
851 */
852
853 for ( heap = 0 ; heap < nr_heaps ; heap++ ) {
854 if ( heaps[ heap ] == NULL ) {
855 (void) memset( max_textures[ heap ].c, 0,
856 sizeof( max_textures[ heap ].c ) );
857 continue;
858 }
859
860 mask = (1U << heaps[ heap ]->logGranularity) - 1;
861
862 if ( 0 ) {
863 fprintf( stderr, "[%s:%d] heap[%u] = %u bytes, mask = 0x%08x\n",
864 __FILE__, __LINE__,
865 heap, heaps[ heap ]->size, mask );
866 }
867
868 for ( log2_size = max_size ; log2_size > 0 ; log2_size-- ) {
869 unsigned total;
870
871
872 /* Determine the total number of bytes required by a texture of
873 * size log2_size.
874 */
875
876 total = texels_this_map_size( log2_size, dimensions, faces )
877 - texels_this_map_size( log2_size - mipmaps_at_once,
878 dimensions, faces );
879 total *= max_bytes_per_texel;
880 total = (total + mask) & ~mask;
881
882 /* The number of textures of a given size that will fit in a heap
883 * is equal to the size of the heap divided by the size of the
884 * texture.
885 */
886
887 max_textures[ heap ].c[ log2_size ] = heaps[ heap ]->size / total;
888
889 if ( 0 ) {
890 fprintf( stderr, "[%s:%d] max_textures[%u].c[%02u] "
891 "= 0x%08x / 0x%08x "
892 "= %u (%u)\n",
893 __FILE__, __LINE__,
894 heap, log2_size,
895 heaps[ heap ]->size, total,
896 heaps[ heap ]->size / total,
897 max_textures[ heap ].c[ log2_size ] );
898 }
899 }
900 }
901 }
902
903
904 static unsigned
905 get_max_size( unsigned nr_heaps,
906 unsigned texture_units,
907 unsigned max_size,
908 int all_textures_one_heap,
909 struct maps_per_heap * max_textures )
910 {
911 unsigned heap;
912 unsigned log2_size;
913
914
915 /* Determine the largest texture size such that a texture of that size
916 * can be bound to each texture unit at the same time. Some hardware
917 * may require that all textures be in the same texture heap for
918 * multitexturing.
919 */
920
921 for ( log2_size = max_size ; log2_size > 0 ; log2_size-- ) {
922 unsigned total = 0;
923
924 for ( heap = 0 ; heap < nr_heaps ; heap++ )
925 {
926 total += max_textures[ heap ].c[ log2_size ];
927
928 if ( 0 ) {
929 fprintf( stderr, "[%s:%d] max_textures[%u].c[%02u] = %u, "
930 "total = %u\n", __FILE__, __LINE__, heap, log2_size,
931 max_textures[ heap ].c[ log2_size ], total );
932 }
933
934 if ( (max_textures[ heap ].c[ log2_size ] >= texture_units)
935 || (!all_textures_one_heap && (total >= texture_units)) ) {
936 /* The number of mipmap levels is the log-base-2 of the
937 * maximum texture size plus 1. If the maximum texture size
938 * is 1x1, the log-base-2 is 0 and 1 mipmap level (the base
939 * level) is available.
940 */
941
942 return log2_size + 1;
943 }
944 }
945 }
946
947 /* This should NEVER happen. It should always be possible to have at
948 * *least* a 1x1 texture in memory!
949 */
950 assert( log2_size != 0 );
951 return 0;
952 }
953
954 #define SET_MAX(f,v) \
955 do { if ( max_sizes[v] != 0 ) { limits-> f = max_sizes[v]; } } while( 0 )
956
957 #define SET_MAX_RECT(f,v) \
958 do { if ( max_sizes[v] != 0 ) { limits-> f = 1 << (max_sizes[v] - 1); } } while( 0 )
959
960
961 /**
962 * Given the amount of texture memory, the number of texture units, and the
963 * maximum size of a texel, calculate the maximum texture size the driver can
964 * advertise.
965 *
966 * \param heaps Texture heaps for this card
967 * \param nr_heap Number of texture heaps
968 * \param limits OpenGL contants. MaxTextureUnits must be set.
969 * \param max_bytes_per_texel Maximum size of a single texel, in bytes
970 * \param max_2D_size \f$\log_2\f$ of the maximum 2D texture size (i.e.,
971 * 1024x1024 textures, this would be 10)
972 * \param max_3D_size \f$\log_2\f$ of the maximum 3D texture size (i.e.,
973 * 1024x1024x1024 textures, this would be 10)
974 * \param max_cube_size \f$\log_2\f$ of the maximum cube texture size (i.e.,
975 * 1024x1024 textures, this would be 10)
976 * \param max_rect_size \f$\log_2\f$ of the maximum texture rectangle size
977 * (i.e., 1024x1024 textures, this would be 10). This is a power-of-2
978 * even though texture rectangles need not be a power-of-2.
979 * \param mipmaps_at_once Total number of mipmaps that can be used
980 * at one time. For most hardware this will be \f$\c max_size + 1\f$.
981 * For hardware that does not support mipmapping, this will be 1.
982 * \param all_textures_one_heap True if the hardware requires that all
983 * textures be in a single texture heap for multitexturing.
984 * \param allow_larger_textures 0 conservative, 1 calculate limits
985 * so at least one worst-case texture can fit, 2 just use hw limits.
986 */
987
988 void
989 driCalculateMaxTextureLevels( driTexHeap * const * heaps,
990 unsigned nr_heaps,
991 struct gl_constants * limits,
992 unsigned max_bytes_per_texel,
993 unsigned max_2D_size,
994 unsigned max_3D_size,
995 unsigned max_cube_size,
996 unsigned max_rect_size,
997 unsigned mipmaps_at_once,
998 int all_textures_one_heap,
999 int allow_larger_textures )
1000 {
1001 struct maps_per_heap max_textures[8];
1002 unsigned i;
1003 const unsigned dimensions[4] = { 2, 3, 2, 2 };
1004 const unsigned faces[4] = { 1, 1, 6, 1 };
1005 unsigned max_sizes[4];
1006 unsigned mipmaps[4];
1007
1008
1009 max_sizes[0] = max_2D_size;
1010 max_sizes[1] = max_3D_size;
1011 max_sizes[2] = max_cube_size;
1012 max_sizes[3] = max_rect_size;
1013
1014 mipmaps[0] = mipmaps_at_once;
1015 mipmaps[1] = mipmaps_at_once;
1016 mipmaps[2] = mipmaps_at_once;
1017 mipmaps[3] = 1;
1018
1019
1020 /* Calculate the maximum number of texture levels in two passes. The
1021 * first pass determines how many textures of each power-of-two size
1022 * (including all mipmap levels for that size) can fit in each texture
1023 * heap. The second pass finds the largest texture size that allows
1024 * a texture of that size to be bound to every texture unit.
1025 */
1026
1027 for ( i = 0 ; i < 4 ; i++ ) {
1028 if ( (allow_larger_textures != 2) && (max_sizes[ i ] != 0) ) {
1029 fill_in_maximums( heaps, nr_heaps, max_bytes_per_texel,
1030 max_sizes[ i ], mipmaps[ i ],
1031 dimensions[ i ], faces[ i ],
1032 max_textures );
1033
1034 max_sizes[ i ] = get_max_size( nr_heaps,
1035 allow_larger_textures == 1 ?
1036 1 : limits->MaxTextureUnits,
1037 max_sizes[ i ],
1038 all_textures_one_heap,
1039 max_textures );
1040 }
1041 else if (max_sizes[ i ] != 0) {
1042 max_sizes[ i ] += 1;
1043 }
1044 }
1045
1046 SET_MAX( MaxTextureLevels, 0 );
1047 SET_MAX( Max3DTextureLevels, 1 );
1048 SET_MAX( MaxCubeTextureLevels, 2 );
1049 SET_MAX_RECT( MaxTextureRectSize, 3 );
1050 }
1051
1052
1053
1054
1055 /**
1056 * Perform initial binding of default textures objects on a per unit, per
1057 * texture target basis.
1058 *
1059 * \param ctx Current OpenGL context
1060 * \param swapped List of swapped-out textures
1061 * \param targets Bit-mask of value texture targets
1062 */
1063
1064 void driInitTextureObjects( GLcontext *ctx, driTextureObject * swapped,
1065 GLuint targets )
1066 {
1067 struct gl_texture_object *texObj;
1068 GLuint tmp = ctx->Texture.CurrentUnit;
1069 unsigned i;
1070
1071
1072 for ( i = 0 ; i < ctx->Const.MaxTextureUnits ; i++ ) {
1073 ctx->Texture.CurrentUnit = i;
1074
1075 if ( (targets & DRI_TEXMGR_DO_TEXTURE_1D) != 0 ) {
1076 texObj = ctx->Texture.Unit[i].Current1D;
1077 ctx->Driver.BindTexture( ctx, GL_TEXTURE_1D, texObj );
1078 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1079 }
1080
1081 if ( (targets & DRI_TEXMGR_DO_TEXTURE_2D) != 0 ) {
1082 texObj = ctx->Texture.Unit[i].Current2D;
1083 ctx->Driver.BindTexture( ctx, GL_TEXTURE_2D, texObj );
1084 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1085 }
1086
1087 if ( (targets & DRI_TEXMGR_DO_TEXTURE_3D) != 0 ) {
1088 texObj = ctx->Texture.Unit[i].Current3D;
1089 ctx->Driver.BindTexture( ctx, GL_TEXTURE_3D, texObj );
1090 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1091 }
1092
1093 if ( (targets & DRI_TEXMGR_DO_TEXTURE_CUBE) != 0 ) {
1094 texObj = ctx->Texture.Unit[i].CurrentCubeMap;
1095 ctx->Driver.BindTexture( ctx, GL_TEXTURE_CUBE_MAP_ARB, texObj );
1096 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1097 }
1098
1099 if ( (targets & DRI_TEXMGR_DO_TEXTURE_RECT) != 0 ) {
1100 texObj = ctx->Texture.Unit[i].CurrentRect;
1101 ctx->Driver.BindTexture( ctx, GL_TEXTURE_RECTANGLE_NV, texObj );
1102 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1103 }
1104 }
1105
1106 ctx->Texture.CurrentUnit = tmp;
1107 }
1108
1109
1110
1111
1112 /**
1113 * Verify that the specified texture is in the specificed heap.
1114 *
1115 * \param tex Texture to be tested.
1116 * \param heap Texture memory heap to be tested.
1117 * \return True if the texture is in the heap, false otherwise.
1118 */
1119
1120 static GLboolean
1121 check_in_heap( const driTextureObject * tex, const driTexHeap * heap )
1122 {
1123 #if 1
1124 return tex->heap == heap;
1125 #else
1126 driTextureObject * curr;
1127
1128 foreach( curr, & heap->texture_objects ) {
1129 if ( curr == tex ) {
1130 break;
1131 }
1132 }
1133
1134 return curr == tex;
1135 #endif
1136 }
1137
1138
1139
1140 /****************************************************************************/
1141 /**
1142 * Validate the consistency of a set of texture heaps.
1143 * Original version by Keith Whitwell in r200/r200_sanity.c.
1144 */
1145
1146 GLboolean
1147 driValidateTextureHeaps( driTexHeap * const * texture_heaps,
1148 unsigned nr_heaps, const driTextureObject * swapped )
1149 {
1150 driTextureObject *t;
1151 unsigned i;
1152
1153 for ( i = 0 ; i < nr_heaps ; i++ ) {
1154 int last_end = 0;
1155 unsigned textures_in_heap = 0;
1156 unsigned blocks_in_mempool = 0;
1157 const driTexHeap * heap = texture_heaps[i];
1158 const struct mem_block *p = heap->memory_heap;
1159
1160 /* Check each texture object has a MemBlock, and is linked into
1161 * the correct heap.
1162 *
1163 * Check the texobj base address corresponds to the MemBlock
1164 * range. Check the texobj size (recalculate?) fits within
1165 * the MemBlock.
1166 *
1167 * Count the number of texobj's using this heap.
1168 */
1169
1170 foreach ( t, &heap->texture_objects ) {
1171 if ( !check_in_heap( t, heap ) ) {
1172 fprintf( stderr, "%s memory block for texture object @ %p not "
1173 "found in heap #%d\n",
1174 __FUNCTION__, (void *)t, i );
1175 return GL_FALSE;
1176 }
1177
1178
1179 if ( t->totalSize > t->memBlock->size ) {
1180 fprintf( stderr, "%s: Memory block for texture object @ %p is "
1181 "only %u bytes, but %u are required\n",
1182 __FUNCTION__, (void *)t, t->totalSize, t->memBlock->size );
1183 return GL_FALSE;
1184 }
1185
1186 textures_in_heap++;
1187 }
1188
1189 /* Validate the contents of the heap:
1190 * - Ordering
1191 * - Overlaps
1192 * - Bounds
1193 */
1194
1195 while ( p != NULL ) {
1196 if (p->reserved) {
1197 fprintf( stderr, "%s: Block (%08x,%x), is reserved?!\n",
1198 __FUNCTION__, p->ofs, p->size );
1199 return GL_FALSE;
1200 }
1201
1202 if (p->ofs != last_end) {
1203 fprintf( stderr, "%s: blocks_in_mempool = %d, last_end = %d, p->ofs = %d\n",
1204 __FUNCTION__, blocks_in_mempool, last_end, p->ofs );
1205 return GL_FALSE;
1206 }
1207
1208 if (!p->reserved && !p->free) {
1209 blocks_in_mempool++;
1210 }
1211
1212 last_end = p->ofs + p->size;
1213 p = p->next;
1214 }
1215
1216 if (textures_in_heap != blocks_in_mempool) {
1217 fprintf( stderr, "%s: Different number of textures objects (%u) and "
1218 "inuse memory blocks (%u)\n",
1219 __FUNCTION__, textures_in_heap, blocks_in_mempool );
1220 return GL_FALSE;
1221 }
1222
1223 #if 0
1224 fprintf( stderr, "%s: textures_in_heap = %u\n",
1225 __FUNCTION__, textures_in_heap );
1226 #endif
1227 }
1228
1229
1230 /* Check swapped texobj's have zero memblocks
1231 */
1232 i = 0;
1233 foreach ( t, swapped ) {
1234 if ( t->memBlock != NULL ) {
1235 fprintf( stderr, "%s: Swapped texobj %p has non-NULL memblock %p\n",
1236 __FUNCTION__, (void *)t, (void *)t->memBlock );
1237 return GL_FALSE;
1238 }
1239 i++;
1240 }
1241
1242 #if 0
1243 fprintf( stderr, "%s: swapped texture count = %u\n", __FUNCTION__, i );
1244 #endif
1245
1246 return GL_TRUE;
1247 }
1248
1249
1250
1251
1252 /****************************************************************************/
1253 /**
1254 * Compute which mipmap levels that really need to be sent to the hardware.
1255 * This depends on the base image size, GL_TEXTURE_MIN_LOD,
1256 * GL_TEXTURE_MAX_LOD, GL_TEXTURE_BASE_LEVEL, and GL_TEXTURE_MAX_LEVEL.
1257 */
1258
1259 void
1260 driCalculateTextureFirstLastLevel( driTextureObject * t )
1261 {
1262 struct gl_texture_object * const tObj = t->tObj;
1263 const struct gl_texture_image * const baseImage =
1264 tObj->Image[0][tObj->BaseLevel];
1265
1266 /* These must be signed values. MinLod and MaxLod can be negative numbers,
1267 * and having firstLevel and lastLevel as signed prevents the need for
1268 * extra sign checks.
1269 */
1270 int firstLevel;
1271 int lastLevel;
1272
1273 /* Yes, this looks overly complicated, but it's all needed.
1274 */
1275
1276 switch (tObj->Target) {
1277 case GL_TEXTURE_1D:
1278 case GL_TEXTURE_2D:
1279 case GL_TEXTURE_3D:
1280 case GL_TEXTURE_CUBE_MAP:
1281 if (tObj->MinFilter == GL_NEAREST || tObj->MinFilter == GL_LINEAR) {
1282 /* GL_NEAREST and GL_LINEAR only care about GL_TEXTURE_BASE_LEVEL.
1283 */
1284
1285 firstLevel = lastLevel = tObj->BaseLevel;
1286 }
1287 else {
1288 firstLevel = tObj->BaseLevel + (GLint)(tObj->MinLod + 0.5);
1289 firstLevel = MAX2(firstLevel, tObj->BaseLevel);
1290 firstLevel = MIN2(firstLevel, tObj->BaseLevel + baseImage->MaxLog2);
1291 lastLevel = tObj->BaseLevel + (GLint)(tObj->MaxLod + 0.5);
1292 lastLevel = MAX2(lastLevel, t->tObj->BaseLevel);
1293 lastLevel = MIN2(lastLevel, t->tObj->BaseLevel + baseImage->MaxLog2);
1294 lastLevel = MIN2(lastLevel, t->tObj->MaxLevel);
1295 lastLevel = MAX2(firstLevel, lastLevel); /* need at least one level */
1296 }
1297 break;
1298 case GL_TEXTURE_RECTANGLE_NV:
1299 case GL_TEXTURE_4D_SGIS:
1300 firstLevel = lastLevel = 0;
1301 break;
1302 default:
1303 return;
1304 }
1305
1306 /* save these values */
1307 t->firstLevel = firstLevel;
1308 t->lastLevel = lastLevel;
1309 }
1310
1311
1312
1313
1314 /**
1315 * \name DRI texture formats. Pointers initialized to either the big- or
1316 * little-endian Mesa formats.
1317 */
1318 /*@{*/
1319 const struct gl_texture_format *_dri_texformat_rgba8888 = NULL;
1320 const struct gl_texture_format *_dri_texformat_argb8888 = NULL;
1321 const struct gl_texture_format *_dri_texformat_rgb565 = NULL;
1322 const struct gl_texture_format *_dri_texformat_argb4444 = NULL;
1323 const struct gl_texture_format *_dri_texformat_argb1555 = NULL;
1324 const struct gl_texture_format *_dri_texformat_al88 = NULL;
1325 const struct gl_texture_format *_dri_texformat_a8 = &_mesa_texformat_a8;
1326 const struct gl_texture_format *_dri_texformat_ci8 = &_mesa_texformat_ci8;
1327 const struct gl_texture_format *_dri_texformat_i8 = &_mesa_texformat_i8;
1328 const struct gl_texture_format *_dri_texformat_l8 = &_mesa_texformat_l8;
1329 /*@}*/
1330
1331
1332 /**
1333 * Initialize little endian target, host byte order independent texture formats
1334 */
1335 void
1336 driInitTextureFormats(void)
1337 {
1338 const GLuint ui = 1;
1339 const GLubyte littleEndian = *((const GLubyte *) &ui);
1340
1341 if (littleEndian) {
1342 _dri_texformat_rgba8888 = &_mesa_texformat_rgba8888;
1343 _dri_texformat_argb8888 = &_mesa_texformat_argb8888;
1344 _dri_texformat_rgb565 = &_mesa_texformat_rgb565;
1345 _dri_texformat_argb4444 = &_mesa_texformat_argb4444;
1346 _dri_texformat_argb1555 = &_mesa_texformat_argb1555;
1347 _dri_texformat_al88 = &_mesa_texformat_al88;
1348 }
1349 else {
1350 _dri_texformat_rgba8888 = &_mesa_texformat_rgba8888_rev;
1351 _dri_texformat_argb8888 = &_mesa_texformat_argb8888_rev;
1352 _dri_texformat_rgb565 = &_mesa_texformat_rgb565_rev;
1353 _dri_texformat_argb4444 = &_mesa_texformat_argb4444_rev;
1354 _dri_texformat_argb1555 = &_mesa_texformat_argb1555_rev;
1355 _dri_texformat_al88 = &_mesa_texformat_al88_rev;
1356 }
1357 }