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