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