Update DRI drivers to current DRI CVS and make them work.
[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
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 unsigned
68 driLog2( unsigned n )
69 {
70 unsigned log2;
71
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 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].in_use = 1;
250 list[i].age = heap->local_age;
251
252 /* remove_from_list(i)
253 */
254 list[(unsigned)list[i].next].prev = list[i].prev;
255 list[(unsigned)list[i].prev].next = list[i].next;
256
257 /* insert_at_head(list, i)
258 */
259 list[i].prev = heap->nrRegions;
260 list[i].next = list[heap->nrRegions].next;
261 list[(unsigned)list[heap->nrRegions].next].prev = i;
262 list[heap->nrRegions].next = i;
263 }
264
265 if ( 0 ) {
266 printGlobalLRU( heap, __FUNCTION__ );
267 printLocalLRU( heap, __FUNCTION__ );
268 }
269 }
270 }
271
272
273
274
275 /**
276 * Keep track of swapped out texture objects.
277 *
278 * \param t Texture object to be "swapped" out of its texture heap
279 */
280
281 void driSwapOutTextureObject( driTextureObject * t )
282 {
283 unsigned face;
284
285
286 if ( t->memBlock != NULL ) {
287 assert( t->heap != NULL );
288 mmFreeMem( t->memBlock );
289 t->memBlock = NULL;
290
291 if (t->timestamp > t->heap->timestamp)
292 t->heap->timestamp = t->timestamp;
293
294 t->heap->texture_swaps[0]++;
295 move_to_tail( t->heap->swapped_objects, t );
296 t->heap = NULL;
297 }
298 else {
299 assert( t->heap == NULL );
300 }
301
302
303 for ( face = 0 ; face < 6 ; face++ ) {
304 t->dirty_images[face] = ~0;
305 }
306 }
307
308
309
310
311 /**
312 * Destroy hardware state associated with texture \a t. Calls the
313 * \a destroy_texture_object method associated with the heap from which
314 * \a t was allocated.
315 *
316 * \param t Texture object to be destroyed
317 */
318
319 void driDestroyTextureObject( driTextureObject * t )
320 {
321 driTexHeap * heap;
322
323
324 if ( 0 ) {
325 fprintf( stderr, "[%s:%d] freeing %p (tObj = %p, DriverData = %p)\n",
326 __FILE__, __LINE__,
327 (void *)t,
328 (void *)((t != NULL) ? t->tObj : NULL),
329 (void *)((t != NULL && t->tObj != NULL) ? t->tObj->DriverData : NULL ));
330 }
331
332 if ( t != NULL ) {
333 if ( t->memBlock ) {
334 heap = t->heap;
335 assert( heap != NULL );
336
337 heap->texture_swaps[0]++;
338
339 mmFreeMem( t->memBlock );
340 t->memBlock = NULL;
341
342 if (t->timestamp > t->heap->timestamp)
343 t->heap->timestamp = t->timestamp;
344
345 heap->destroy_texture_object( heap->driverContext, t );
346 t->heap = NULL;
347 }
348
349 if ( t->tObj != NULL ) {
350 assert( t->tObj->DriverData == t );
351 t->tObj->DriverData = NULL;
352 }
353
354 remove_from_list( t );
355 FREE( t );
356 }
357
358 if ( 0 ) {
359 fprintf( stderr, "[%s:%d] done freeing %p\n", __FILE__, __LINE__, (void *)t );
360 }
361 }
362
363
364
365
366 /**
367 * Update the local heap's representation of texture memory based on
368 * data in the SAREA. This is done each time it is detected that some other
369 * direct rendering client has held the lock. This pertains to both our local
370 * textures and the textures belonging to other clients. Keep track of other
371 * client's textures by pushing a placeholder texture onto the LRU list --
372 * these are denoted by \a tObj being \a NULL.
373 *
374 * \param heap Heap whose state is to be updated
375 * \param offset Byte offset in the heap that has been stolen
376 * \param size Size, in bytes, of the stolen block
377 * \param in_use Non-zero if the block is in-use by another context
378 */
379
380 static void driTexturesGone( driTexHeap * heap, int offset, int size,
381 int in_use )
382 {
383 driTextureObject * t;
384 driTextureObject * tmp;
385
386
387 foreach_s ( t, tmp, & heap->texture_objects ) {
388 if ( (t->memBlock->ofs < (offset + size))
389 && ((t->memBlock->ofs + t->memBlock->size) > offset) ) {
390 /* It overlaps - kick it out. If the texture object is just a
391 * place holder, then destroy it all together. Otherwise, mark
392 * it as being swapped out.
393 */
394
395 if ( t->tObj != NULL ) {
396 driSwapOutTextureObject( t );
397 }
398 else {
399 if ( in_use &&
400 offset == t->memBlock->ofs && size == t->memBlock->size ) {
401 /* Matching placeholder already exists */
402 return;
403 } else {
404 driDestroyTextureObject( t );
405 }
406 }
407 }
408 }
409
410
411 if ( in_use ) {
412 t = (driTextureObject *) CALLOC( heap->texture_object_size );
413 if ( t == NULL ) return;
414
415 t->memBlock = mmAllocMem( heap->memory_heap, size, 0, offset );
416 if ( t->memBlock == NULL ) {
417 fprintf( stderr, "Couldn't alloc placeholder: heap %u sz %x ofs %x\n", heap->heapId,
418 (int)size, (int)offset );
419 mmDumpMemInfo( heap->memory_heap );
420 return;
421 }
422 t->heap = heap;
423 insert_at_head( & heap->texture_objects, t );
424 }
425 }
426
427
428
429
430 /**
431 * Called by the client on lock contention to determine whether textures have
432 * been stolen. If another client has modified a region in which we have
433 * textures, then we need to figure out which of our textures have been
434 * removed and update our global LRU.
435 *
436 * \param heap Texture heap to be updated
437 */
438
439 void driAgeTextures( driTexHeap * heap )
440 {
441 drmTextureRegionPtr list = heap->global_regions;
442 unsigned sz = 1U << (heap->logGranularity);
443 unsigned i, nr = 0;
444
445
446 /* Have to go right round from the back to ensure stuff ends up
447 * LRU in the local list... Fix with a cursor pointer.
448 */
449
450 for (i = list[heap->nrRegions].prev ;
451 i != heap->nrRegions && nr < heap->nrRegions ;
452 i = list[i].prev, nr++) {
453 /* If switching texturing schemes, then the SAREA might not have been
454 * properly cleared, so we need to reset the global texture LRU.
455 */
456
457 if ( (i * sz) > heap->size ) {
458 nr = heap->nrRegions;
459 break;
460 }
461
462 if (list[i].age > heap->local_age)
463 driTexturesGone( heap, i * sz, sz, list[i].in_use);
464 }
465
466 /* Loop or uninitialized heap detected. Reset.
467 */
468
469 if (nr == heap->nrRegions) {
470 driTexturesGone( heap, 0, heap->size, 0);
471 resetGlobalLRU( heap );
472 }
473
474 if ( 0 ) {
475 printGlobalLRU( heap, __FUNCTION__ );
476 printLocalLRU( heap, __FUNCTION__ );
477 }
478
479 heap->local_age = heap->global_age[0];
480 }
481
482
483
484
485 /**
486 * Allocate memory from a texture heap to hold a texture object. This
487 * routine will attempt to allocate memory for the texture from the heaps
488 * specified by \c heap_array in order. That is, first it will try to
489 * allocate from \c heap_array[0], then \c heap_array[1], and so on.
490 *
491 * \param heap_array Array of pointers to texture heaps to use
492 * \param nr_heaps Number of heap pointer in \a heap_array
493 * \param t Texture object for which space is needed
494 * \return The ID of the heap from which memory was allocated, or -1 if
495 * memory could not be allocated.
496 *
497 * \bug The replacement policy implemented by this function is horrible.
498 */
499
500
501 int
502 driAllocateTexture( driTexHeap * const * heap_array, unsigned nr_heaps,
503 driTextureObject * t )
504 {
505 driTexHeap * heap;
506 driTextureObject * temp;
507 driTextureObject * cursor;
508 unsigned id;
509
510
511 /* In case it already has texture space, initialize heap. This also
512 * prevents GCC from issuing a warning that heap might be used
513 * uninitialized.
514 */
515
516 heap = t->heap;
517
518
519 /* Run through each of the existing heaps and try to allocate a buffer
520 * to hold the texture.
521 */
522
523 for ( id = 0 ; (t->memBlock == NULL) && (id < nr_heaps) ; id++ ) {
524 heap = heap_array[ id ];
525 if ( heap != NULL ) {
526 t->memBlock = mmAllocMem( heap->memory_heap, t->totalSize,
527 heap->alignmentShift, 0 );
528 }
529 }
530
531
532 /* Kick textures out until the requested texture fits.
533 */
534
535 if ( t->memBlock == NULL ) {
536 for ( id = 0 ; (t->memBlock == NULL) && (id < nr_heaps) ; id++ ) {
537 heap = heap_array[ id ];
538 if ( t->totalSize <= heap->size ) {
539
540 for ( cursor = heap->texture_objects.prev, temp = cursor->prev;
541 cursor != &heap->texture_objects ;
542 cursor = temp, temp = cursor->prev ) {
543
544 /* The the LRU element. If the texture is bound to one of
545 * the texture units, then we cannot kick it out.
546 */
547 if ( cursor->bound /* || cursor->reserved */ ) {
548 continue;
549 }
550
551 /* If this is a placeholder, there's no need to keep it */
552 if (cursor->tObj)
553 driSwapOutTextureObject( cursor );
554 else
555 driDestroyTextureObject( cursor );
556
557 t->memBlock = mmAllocMem( heap->memory_heap, t->totalSize,
558 heap->alignmentShift, 0 );
559
560 if (t->memBlock)
561 break;
562 }
563 } /* if ( t->totalSize <= heap->size ) ... */
564 }
565 }
566
567
568 if ( t->memBlock != NULL ) {
569 /* id and heap->heapId may or may not be the same value here.
570 */
571
572 assert( heap != NULL );
573 assert( (t->heap == NULL) || (t->heap == heap) );
574
575 t->heap = heap;
576 return heap->heapId;
577 }
578 else {
579 assert( t->heap == NULL );
580
581 fprintf( stderr, "[%s:%d] unable to allocate texture\n",
582 __FUNCTION__, __LINE__ );
583 return -1;
584 }
585 }
586
587
588
589
590
591
592 /**
593 * Set the location where the texture-swap counter is stored.
594 */
595
596 void
597 driSetTextureSwapCounterLocation( driTexHeap * heap, unsigned * counter )
598 {
599 heap->texture_swaps = (counter == NULL) ? & dummy_swap_counter : counter;
600 }
601
602
603
604
605 /**
606 * Create a new heap for texture data.
607 *
608 * \param heap_id Device-dependent heap identifier. This value
609 * will returned by driAllocateTexture when memory
610 * is allocated from this heap.
611 * \param context Device-dependent driver context. This is
612 * supplied as the first parameter to the
613 * \c destroy_tex_obj function.
614 * \param size Size, in bytes, of the texture region
615 * \param alignmentShift Alignment requirement for textures. If textures
616 * must be allocated on a 4096 byte boundry, this
617 * would be 12.
618 * \param nr_regions Number of regions into which this texture space
619 * should be partitioned
620 * \param global_regions Array of \c drmTextureRegion structures in the SAREA
621 * \param global_age Pointer to the global texture age in the SAREA
622 * \param swapped_objects Pointer to the list of texture objects that are
623 * not in texture memory (i.e., have been swapped
624 * out).
625 * \param texture_object_size Size, in bytes, of a device-dependent texture
626 * object
627 * \param destroy_tex_obj Function used to destroy a device-dependent
628 * texture object
629 *
630 * \sa driDestroyTextureHeap
631 */
632
633 driTexHeap *
634 driCreateTextureHeap( unsigned heap_id, void * context, unsigned size,
635 unsigned alignmentShift, unsigned nr_regions,
636 drmTextureRegionPtr global_regions, unsigned * global_age,
637 driTextureObject * swapped_objects,
638 unsigned texture_object_size,
639 destroy_texture_object_t * destroy_tex_obj
640 )
641 {
642 driTexHeap * heap;
643 unsigned l;
644
645
646 if ( 0 )
647 fprintf( stderr, "%s( %u, %p, %u, %u, %u )\n",
648 __FUNCTION__,
649 heap_id, (void *)context, size, alignmentShift, nr_regions );
650
651 heap = (driTexHeap *) CALLOC( sizeof( driTexHeap ) );
652 if ( heap != NULL ) {
653 l = driLog2( (size - 1) / nr_regions );
654 if ( l < alignmentShift )
655 {
656 l = alignmentShift;
657 }
658
659 heap->logGranularity = l;
660 heap->size = size & ~((1L << l) - 1);
661
662 heap->memory_heap = mmInit( 0, heap->size );
663 if ( heap->memory_heap != NULL ) {
664 heap->heapId = heap_id;
665 heap->driverContext = context;
666
667 heap->alignmentShift = alignmentShift;
668 heap->nrRegions = nr_regions;
669 heap->global_regions = global_regions;
670 heap->global_age = global_age;
671 heap->swapped_objects = swapped_objects;
672 heap->texture_object_size = texture_object_size;
673 heap->destroy_texture_object = destroy_tex_obj;
674
675 /* Force global heap init */
676 if (heap->global_age == 0)
677 heap->local_age = ~0;
678 else
679 heap->local_age = 0;
680
681 make_empty_list( & heap->texture_objects );
682 driSetTextureSwapCounterLocation( heap, NULL );
683 }
684 else {
685 FREE( heap );
686 heap = NULL;
687 }
688 }
689
690
691 if ( 0 )
692 fprintf( stderr, "%s returning %p\n", __FUNCTION__, (void *)heap );
693
694 return heap;
695 }
696
697
698
699
700 /** Destroys a texture heap
701 *
702 * \param heap Texture heap to be destroyed
703 */
704
705 void
706 driDestroyTextureHeap( driTexHeap * heap )
707 {
708 driTextureObject * t;
709 driTextureObject * temp;
710
711
712 if ( heap != NULL ) {
713 foreach_s( t, temp, & heap->texture_objects ) {
714 driDestroyTextureObject( t );
715 }
716 foreach_s( t, temp, heap->swapped_objects ) {
717 driDestroyTextureObject( t );
718 }
719
720 mmDestroy( heap->memory_heap );
721 FREE( heap );
722 }
723 }
724
725
726
727
728 /****************************************************************************/
729 /**
730 * Determine how many texels (including all mipmap levels) would be required
731 * for a texture map of size \f$2^^\c base_size_log2\f$ would require.
732 *
733 * \param base_size_log2 \f$log_2\f$ of the size of a side of the texture
734 * \param dimensions Number of dimensions of the texture. Either 2 or 3.
735 * \param faces Number of faces of the texture. Either 1 or 6 (for cube maps).
736 * \return Number of texels
737 */
738
739 static unsigned
740 texels_this_map_size( int base_size_log2, unsigned dimensions, unsigned faces )
741 {
742 unsigned texels;
743
744
745 assert( (faces == 1) || (faces == 6) );
746 assert( (dimensions == 2) || (dimensions == 3) );
747
748 texels = 0;
749 if ( base_size_log2 >= 0 ) {
750 texels = (1U << (dimensions * base_size_log2));
751
752 /* See http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg03636.html
753 * for the complete explaination of why this formulation is used.
754 * Basically, the smaller mipmap levels sum to 0.333 the size of the
755 * level 0 map. The total size is therefore the size of the map
756 * multipled by 1.333. The +2 is there to round up.
757 */
758
759 texels = (texels * 4 * faces + 2) / 3;
760 }
761
762 return texels;
763 }
764
765
766
767
768 struct maps_per_heap {
769 unsigned c[32];
770 };
771
772 static void
773 fill_in_maximums( driTexHeap * const * heaps, unsigned nr_heaps,
774 unsigned max_bytes_per_texel, unsigned max_size,
775 unsigned mipmaps_at_once, unsigned dimensions,
776 unsigned faces, struct maps_per_heap * max_textures )
777 {
778 unsigned heap;
779 unsigned log2_size;
780 unsigned mask;
781
782
783 /* Determine how many textures of each size can be stored in each
784 * texture heap.
785 */
786
787 for ( heap = 0 ; heap < nr_heaps ; heap++ ) {
788 if ( heaps[ heap ] == NULL ) {
789 (void) memset( max_textures[ heap ].c, 0,
790 sizeof( max_textures[ heap ].c ) );
791 continue;
792 }
793
794 mask = (1U << heaps[ heap ]->logGranularity) - 1;
795
796 if ( 0 ) {
797 fprintf( stderr, "[%s:%d] heap[%u] = %u bytes, mask = 0x%08x\n",
798 __FILE__, __LINE__,
799 heap, heaps[ heap ]->size, mask );
800 }
801
802 for ( log2_size = max_size ; log2_size > 0 ; log2_size-- ) {
803 unsigned total;
804
805
806 /* Determine the total number of bytes required by a texture of
807 * size log2_size.
808 */
809
810 total = texels_this_map_size( log2_size, dimensions, faces )
811 - texels_this_map_size( log2_size - mipmaps_at_once,
812 dimensions, faces );
813 total *= max_bytes_per_texel;
814 total = (total + mask) & ~mask;
815
816 /* The number of textures of a given size that will fit in a heap
817 * is equal to the size of the heap divided by the size of the
818 * texture.
819 */
820
821 max_textures[ heap ].c[ log2_size ] = heaps[ heap ]->size / total;
822
823 if ( 0 ) {
824 fprintf( stderr, "[%s:%d] max_textures[%u].c[%02u] "
825 "= 0x%08x / 0x%08x "
826 "= %u (%u)\n",
827 __FILE__, __LINE__,
828 heap, log2_size,
829 heaps[ heap ]->size, total,
830 heaps[ heap ]->size / total,
831 max_textures[ heap ].c[ log2_size ] );
832 }
833 }
834 }
835 }
836
837
838 static unsigned
839 get_max_size( unsigned nr_heaps,
840 unsigned texture_units,
841 unsigned max_size,
842 int all_textures_one_heap,
843 struct maps_per_heap * max_textures )
844 {
845 unsigned heap;
846 unsigned log2_size;
847
848
849 /* Determine the largest texture size such that a texture of that size
850 * can be bound to each texture unit at the same time. Some hardware
851 * may require that all textures be in the same texture heap for
852 * multitexturing.
853 */
854
855 for ( log2_size = max_size ; log2_size > 0 ; log2_size-- ) {
856 unsigned total = 0;
857
858 for ( heap = 0 ; heap < nr_heaps ; heap++ )
859 {
860 total += max_textures[ heap ].c[ log2_size ];
861
862 if ( 0 ) {
863 fprintf( stderr, "[%s:%d] max_textures[%u].c[%02u] = %u, "
864 "total = %u\n", __FILE__, __LINE__, heap, log2_size,
865 max_textures[ heap ].c[ log2_size ], total );
866 }
867
868 if ( (max_textures[ heap ].c[ log2_size ] >= texture_units)
869 || (!all_textures_one_heap && (total >= texture_units)) ) {
870 /* The number of mipmap levels is the log-base-2 of the
871 * maximum texture size plus 1. If the maximum texture size
872 * is 1x1, the log-base-2 is 0 and 1 mipmap level (the base
873 * level) is available.
874 */
875
876 return log2_size + 1;
877 }
878 }
879 }
880
881 /* This should NEVER happen. It should always be possible to have at
882 * *least* a 1x1 texture in memory!
883 */
884 assert( log2_size != 0 );
885 return 0;
886 }
887
888 #define SET_MAX(f,v) \
889 do { if ( max_sizes[v] != 0 ) { limits-> f = max_sizes[v]; } } while( 0 )
890
891 #define SET_MAX_RECT(f,v) \
892 do { if ( max_sizes[v] != 0 ) { limits-> f = 1 << max_sizes[v]; } } while( 0 )
893
894
895 /**
896 * Given the amount of texture memory, the number of texture units, and the
897 * maximum size of a texel, calculate the maximum texture size the driver can
898 * advertise.
899 *
900 * \param heaps Texture heaps for this card
901 * \param nr_heap Number of texture heaps
902 * \param limits OpenGL contants. MaxTextureUnits must be set.
903 * \param max_bytes_per_texel Maximum size of a single texel, in bytes
904 * \param max_2D_size \f$\log_2\f$ of the maximum 2D texture size (i.e.,
905 * 1024x1024 textures, this would be 10)
906 * \param max_3D_size \f$\log_2\f$ of the maximum 3D texture size (i.e.,
907 * 1024x1024x1024 textures, this would be 10)
908 * \param max_cube_size \f$\log_2\f$ of the maximum cube texture size (i.e.,
909 * 1024x1024 textures, this would be 10)
910 * \param max_rect_size \f$\log_2\f$ of the maximum texture rectangle size
911 * (i.e., 1024x1024 textures, this would be 10). This is a power-of-2
912 * even though texture rectangles need not be a power-of-2.
913 * \param mipmaps_at_once Total number of mipmaps that can be used
914 * at one time. For most hardware this will be \f$\c max_size + 1\f$.
915 * For hardware that does not support mipmapping, this will be 1.
916 * \param all_textures_one_heap True if the hardware requires that all
917 * textures be in a single texture heap for multitexturing.
918 */
919
920 void
921 driCalculateMaxTextureLevels( driTexHeap * const * heaps,
922 unsigned nr_heaps,
923 struct gl_constants * limits,
924 unsigned max_bytes_per_texel,
925 unsigned max_2D_size,
926 unsigned max_3D_size,
927 unsigned max_cube_size,
928 unsigned max_rect_size,
929 unsigned mipmaps_at_once,
930 int all_textures_one_heap )
931 {
932 struct maps_per_heap max_textures[8];
933 unsigned i;
934 const unsigned dimensions[4] = { 2, 3, 2, 2 };
935 const unsigned faces[4] = { 1, 1, 6, 1 };
936 unsigned max_sizes[4];
937 unsigned mipmaps[4];
938
939
940 max_sizes[0] = max_2D_size;
941 max_sizes[1] = max_3D_size;
942 max_sizes[2] = max_cube_size;
943 max_sizes[3] = max_rect_size;
944
945 mipmaps[0] = mipmaps_at_once;
946 mipmaps[1] = mipmaps_at_once;
947 mipmaps[2] = 1;
948 mipmaps[3] = mipmaps_at_once;
949
950
951 /* Calculate the maximum number of texture levels in two passes. The
952 * first pass determines how many textures of each power-of-two size
953 * (including all mipmap levels for that size) can fit in each texture
954 * heap. The second pass finds the largest texture size that allows
955 * a texture of that size to be bound to every texture unit.
956 */
957
958 for ( i = 0 ; i < 4 ; i++ ) {
959 if ( max_sizes[ i ] != 0 ) {
960 fill_in_maximums( heaps, nr_heaps, max_bytes_per_texel,
961 max_sizes[ i ], mipmaps[ i ],
962 dimensions[ i ], faces[ i ],
963 max_textures );
964
965 max_sizes[ i ] = get_max_size( nr_heaps,
966 limits->MaxTextureUnits,
967 max_sizes[ i ],
968 all_textures_one_heap,
969 max_textures );
970 }
971 }
972
973 SET_MAX( MaxTextureLevels, 0 );
974 SET_MAX( Max3DTextureLevels, 1 );
975 SET_MAX( MaxCubeTextureLevels, 2 );
976 SET_MAX_RECT( MaxTextureRectSize, 3 );
977 }
978
979
980
981
982 /**
983 * Perform initial binding of default textures objects on a per unit, per
984 * texture target basis.
985 *
986 * \param ctx Current OpenGL context
987 * \param swapped List of swapped-out textures
988 * \param targets Bit-mask of value texture targets
989 */
990
991 void driInitTextureObjects( GLcontext *ctx, driTextureObject * swapped,
992 GLuint targets )
993 {
994 struct gl_texture_object *texObj;
995 GLuint tmp = ctx->Texture.CurrentUnit;
996 unsigned i;
997
998
999 for ( i = 0 ; i < ctx->Const.MaxTextureUnits ; i++ ) {
1000 ctx->Texture.CurrentUnit = i;
1001
1002 if ( (targets & DRI_TEXMGR_DO_TEXTURE_1D) != 0 ) {
1003 texObj = ctx->Texture.Unit[i].Current1D;
1004 ctx->Driver.BindTexture( ctx, GL_TEXTURE_1D, texObj );
1005 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1006 }
1007
1008 if ( (targets & DRI_TEXMGR_DO_TEXTURE_2D) != 0 ) {
1009 texObj = ctx->Texture.Unit[i].Current2D;
1010 ctx->Driver.BindTexture( ctx, GL_TEXTURE_2D, texObj );
1011 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1012 }
1013
1014 if ( (targets & DRI_TEXMGR_DO_TEXTURE_3D) != 0 ) {
1015 texObj = ctx->Texture.Unit[i].Current3D;
1016 ctx->Driver.BindTexture( ctx, GL_TEXTURE_3D, texObj );
1017 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1018 }
1019
1020 if ( (targets & DRI_TEXMGR_DO_TEXTURE_CUBE) != 0 ) {
1021 texObj = ctx->Texture.Unit[i].CurrentCubeMap;
1022 ctx->Driver.BindTexture( ctx, GL_TEXTURE_CUBE_MAP_ARB, texObj );
1023 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1024 }
1025
1026 if ( (targets & DRI_TEXMGR_DO_TEXTURE_RECT) != 0 ) {
1027 texObj = ctx->Texture.Unit[i].CurrentRect;
1028 ctx->Driver.BindTexture( ctx, GL_TEXTURE_RECTANGLE_NV, texObj );
1029 move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
1030 }
1031 }
1032
1033 ctx->Texture.CurrentUnit = tmp;
1034 }
1035
1036
1037
1038
1039 /**
1040 * Verify that the specified texture is in the specificed heap.
1041 *
1042 * \param tex Texture to be tested.
1043 * \param heap Texture memory heap to be tested.
1044 * \return True if the texture is in the heap, false otherwise.
1045 */
1046
1047 static GLboolean
1048 check_in_heap( const driTextureObject * tex, const driTexHeap * heap )
1049 {
1050 #if 1
1051 return tex->heap == heap;
1052 #else
1053 driTextureObject * curr;
1054
1055 foreach( curr, & heap->texture_objects ) {
1056 if ( curr == tex ) {
1057 break;
1058 }
1059 }
1060
1061 return curr == tex;
1062 #endif
1063 }
1064
1065
1066
1067 /****************************************************************************/
1068 /**
1069 * Validate the consistency of a set of texture heaps.
1070 * Original version by Keith Whitwell in r200/r200_sanity.c.
1071 */
1072
1073 GLboolean
1074 driValidateTextureHeaps( driTexHeap * const * texture_heaps,
1075 unsigned nr_heaps, const driTextureObject * swapped )
1076 {
1077 driTextureObject *t;
1078 unsigned i;
1079
1080 for ( i = 0 ; i < nr_heaps ; i++ ) {
1081 int last_end = 0;
1082 unsigned textures_in_heap = 0;
1083 unsigned blocks_in_mempool = 0;
1084 const driTexHeap * heap = texture_heaps[i];
1085 const memHeap_t * p = heap->memory_heap;
1086
1087 /* Check each texture object has a MemBlock, and is linked into
1088 * the correct heap.
1089 *
1090 * Check the texobj base address corresponds to the MemBlock
1091 * range. Check the texobj size (recalculate???) fits within
1092 * the MemBlock.
1093 *
1094 * Count the number of texobj's using this heap.
1095 */
1096
1097 foreach ( t, &heap->texture_objects ) {
1098 if ( !check_in_heap( t, heap ) ) {
1099 fprintf( stderr, "%s memory block for texture object @ %p not "
1100 "found in heap #%d\n",
1101 __FUNCTION__, (void *)t, i );
1102 return GL_FALSE;
1103 }
1104
1105
1106 if ( t->totalSize > t->memBlock->size ) {
1107 fprintf( stderr, "%s: Memory block for texture object @ %p is "
1108 "only %u bytes, but %u are required\n",
1109 __FUNCTION__, (void *)t, t->totalSize, t->memBlock->size );
1110 return GL_FALSE;
1111 }
1112
1113 textures_in_heap++;
1114 }
1115
1116 /* Validate the contents of the heap:
1117 * - Ordering
1118 * - Overlaps
1119 * - Bounds
1120 */
1121
1122 while ( p != NULL ) {
1123 if (p->reserved) {
1124 fprintf( stderr, "%s: Block (%08x,%x), is reserved?!\n",
1125 __FUNCTION__, p->ofs, p->size );
1126 return GL_FALSE;
1127 }
1128
1129 if (p->ofs != last_end) {
1130 fprintf( stderr, "%s: blocks_in_mempool = %d, last_end = %d, p->ofs = %d\n",
1131 __FUNCTION__, blocks_in_mempool, last_end, p->ofs );
1132 return GL_FALSE;
1133 }
1134
1135 if (!p->reserved && !p->free) {
1136 blocks_in_mempool++;
1137 }
1138
1139 last_end = p->ofs + p->size;
1140 p = p->next;
1141 }
1142
1143 if (textures_in_heap != blocks_in_mempool) {
1144 fprintf( stderr, "%s: Different number of textures objects (%u) and "
1145 "inuse memory blocks (%u)\n",
1146 __FUNCTION__, textures_in_heap, blocks_in_mempool );
1147 return GL_FALSE;
1148 }
1149
1150 #if 0
1151 fprintf( stderr, "%s: textures_in_heap = %u\n",
1152 __FUNCTION__, textures_in_heap );
1153 #endif
1154 }
1155
1156
1157 /* Check swapped texobj's have zero memblocks
1158 */
1159 i = 0;
1160 foreach ( t, swapped ) {
1161 if ( t->memBlock != NULL ) {
1162 fprintf( stderr, "%s: Swapped texobj %p has non-NULL memblock %p\n",
1163 __FUNCTION__, (void *)t, (void *)t->memBlock );
1164 return GL_FALSE;
1165 }
1166 i++;
1167 }
1168
1169 #if 0
1170 fprintf( stderr, "%s: swapped texture count = %u\n", __FUNCTION__, i );
1171 #endif
1172
1173 return GL_TRUE;
1174 }
1175
1176
1177
1178
1179 /****************************************************************************/
1180 /**
1181 * Compute which mipmap levels that really need to be sent to the hardware.
1182 * This depends on the base image size, GL_TEXTURE_MIN_LOD,
1183 * GL_TEXTURE_MAX_LOD, GL_TEXTURE_BASE_LEVEL, and GL_TEXTURE_MAX_LEVEL.
1184 */
1185
1186 void
1187 driCalculateTextureFirstLastLevel( driTextureObject * t )
1188 {
1189 struct gl_texture_object * const tObj = t->tObj;
1190 const struct gl_texture_image * const baseImage =
1191 tObj->Image[tObj->BaseLevel];
1192
1193 /* These must be signed values. MinLod and MaxLod can be negative numbers,
1194 * and having firstLevel and lastLevel as signed prevents the need for
1195 * extra sign checks.
1196 */
1197 int firstLevel;
1198 int lastLevel;
1199
1200 /* Yes, this looks overly complicated, but it's all needed.
1201 */
1202
1203 switch (tObj->Target) {
1204 case GL_TEXTURE_1D:
1205 case GL_TEXTURE_2D:
1206 case GL_TEXTURE_3D:
1207 case GL_TEXTURE_CUBE_MAP:
1208 if (tObj->MinFilter == GL_NEAREST || tObj->MinFilter == GL_LINEAR) {
1209 /* GL_NEAREST and GL_LINEAR only care about GL_TEXTURE_BASE_LEVEL.
1210 */
1211
1212 firstLevel = lastLevel = tObj->BaseLevel;
1213 }
1214 else {
1215 firstLevel = tObj->BaseLevel + (GLint)(tObj->MinLod + 0.5);
1216 firstLevel = MAX2(firstLevel, tObj->BaseLevel);
1217 lastLevel = tObj->BaseLevel + (GLint)(tObj->MaxLod + 0.5);
1218 lastLevel = MAX2(lastLevel, t->tObj->BaseLevel);
1219 lastLevel = MIN2(lastLevel, t->tObj->BaseLevel + baseImage->MaxLog2);
1220 lastLevel = MIN2(lastLevel, t->tObj->MaxLevel);
1221 lastLevel = MAX2(firstLevel, lastLevel); /* need at least one level */
1222 }
1223 break;
1224 case GL_TEXTURE_RECTANGLE_NV:
1225 case GL_TEXTURE_4D_SGIS:
1226 firstLevel = lastLevel = 0;
1227 break;
1228 default:
1229 return;
1230 }
1231
1232 /* save these values */
1233 t->firstLevel = firstLevel;
1234 t->lastLevel = lastLevel;
1235 }