Merge branch 'master' of git+ssh://brianp@git.freedesktop.org/git/mesa/mesa
[mesa.git] / src / mesa / drivers / dri / common / texmem.c
index 48a8f5c74e9459c61dfeab63af2687baf882c45d..b0e8c4c1c2824c871168bfbfd8862c21fd1b586b 100644 (file)
@@ -188,7 +188,7 @@ static void printLocalLRU( driTexHeap * heap, const char *callername  )
 static void printGlobalLRU( driTexHeap * heap, const char *callername )
 {
    drmTextureRegionPtr list = heap->global_regions;
-   int i, j;
+   unsigned int i, j;
 
    fprintf( stderr, "%s in %s:\nGlobal LRU, heap %d list %p:\n", 
            __FUNCTION__, callername, heap->heapId, (void *)list );
@@ -410,11 +410,12 @@ static void driTexturesGone( driTexHeap * heap, int offset, int size,
         fprintf( stderr, "Couldn't alloc placeholder: heap %u sz %x ofs %x\n", heap->heapId,
                  (int)size, (int)offset );
         mmDumpMemInfo( heap->memory_heap );
+        FREE(t);
         return;
       }
       t->heap = heap;
       if (in_use) 
-        t->bound = 99;
+        t->reserved = 1; 
       insert_at_head( & heap->texture_objects, t );
    }
 }
@@ -477,6 +478,8 @@ void driAgeTextures( driTexHeap * heap )
 
 
 
+#define INDEX_ARRAY_SIZE 6 /* I'm not aware of driver with more than 2 heaps */
+
 /**
  * Allocate memory from a texture heap to hold a texture object.  This
  * routine will attempt to allocate memory for the texture from the heaps
@@ -528,35 +531,91 @@ driAllocateTexture( driTexHeap * const * heap_array, unsigned nr_heaps,
     */
 
    if ( t->memBlock == NULL ) {
-      for ( id = 0 ; (t->memBlock == NULL) && (id < nr_heaps) ; id++ ) {
-        heap = heap_array[ id ];
-        if ( t->totalSize <= heap->size ) { 
+      unsigned index[INDEX_ARRAY_SIZE];
+      unsigned nrGoodHeaps = 0;
 
-           for ( cursor = heap->texture_objects.prev, temp = cursor->prev;
-                 cursor != &heap->texture_objects ; 
-                 cursor = temp, temp = cursor->prev ) {
-              
-              /* The the LRU element.  If the texture is bound to one of
-               * the texture units, then we cannot kick it out.
-               */
-              if ( cursor->bound /* || cursor->reserved */ ) {
-                 continue;
-              }
+      /* Trying to avoid dynamic memory allocation. If you have more
+       * heaps, increase INDEX_ARRAY_SIZE. I'm not aware of any
+       * drivers with more than 2 tex heaps. */
+      assert( nr_heaps < INDEX_ARRAY_SIZE );
 
-              /* If this is a placeholder, there's no need to keep it */
-              if (cursor->tObj)
-                  driSwapOutTextureObject( cursor );
-              else
-                  driDestroyTextureObject( cursor );
+      /* Sort large enough heaps by duty. Insertion sort should be
+       * fast enough for such a short array. */
+      for ( id = 0 ; id < nr_heaps ; id++ ) {
+        heap = heap_array[ id ];
 
-              t->memBlock = mmAllocMem( heap->memory_heap, t->totalSize, 
-                                        heap->alignmentShift, 0 );
+        if ( heap != NULL && t->totalSize <= heap->size ) {
+           unsigned j;
 
-              if (t->memBlock)
+           for ( j = 0 ; j < nrGoodHeaps; j++ ) {
+              if ( heap->duty > heap_array[ index[ j ] ]->duty )
                  break;
            }
-        }     /* if ( t->totalSize <= heap->size ) ... */
+
+           if ( j < nrGoodHeaps ) {
+              memmove( &index[ j+1 ], &index[ j ],
+                       sizeof(index[ 0 ]) * (nrGoodHeaps - j) );
+           }
+
+           index[ j ] = id;
+
+           nrGoodHeaps++;
+        }
       }
+
+      for ( id = 0 ; (t->memBlock == NULL) && (id < nrGoodHeaps) ; id++ ) {
+        heap = heap_array[ index[ id ] ];
+
+        for ( cursor = heap->texture_objects.prev, temp = cursor->prev;
+              cursor != &heap->texture_objects ; 
+              cursor = temp, temp = cursor->prev ) {
+              
+           /* The the LRU element.  If the texture is bound to one of
+            * the texture units, then we cannot kick it out.
+            */
+           if ( cursor->bound || cursor->reserved ) {
+              continue;
+           }
+
+           if ( cursor->memBlock )
+              heap->duty -= cursor->memBlock->size;
+
+           /* If this is a placeholder, there's no need to keep it */
+           if (cursor->tObj)
+              driSwapOutTextureObject( cursor );
+           else
+              driDestroyTextureObject( cursor );
+
+           t->memBlock = mmAllocMem( heap->memory_heap, t->totalSize, 
+                                     heap->alignmentShift, 0 );
+
+           if (t->memBlock)
+              break;
+        }
+      }
+
+      /* Rebalance duties. If a heap kicked more data than its duty,
+       * then all other heaps get that amount multiplied with their
+       * relative weight added to their duty. The negative duty is
+       * reset to 0. In the end all heaps have a duty >= 0.
+       *
+       * CAUTION: we must not change the heap pointer here, because it
+       * is used below to update the texture object.
+       */
+      for ( id = 0 ; id < nr_heaps ; id++ )
+        if ( heap_array[ id ] != NULL && heap_array[ id ]->duty < 0) {
+           int duty = -heap_array[ id ]->duty;
+           double weight = heap_array[ id ]->weight;
+           unsigned j;
+
+           for ( j = 0 ; j < nr_heaps ; j++ )
+              if ( j != id && heap_array[ j ] != NULL ) {
+                 heap_array[ j ]->duty += (double) duty *
+                    heap_array[ j ]->weight / weight;
+              }
+
+           heap_array[ id ]->duty = 0;
+        }
    }
 
 
@@ -675,6 +734,9 @@ driCreateTextureHeap( unsigned heap_id, void * context, unsigned size,
 
         make_empty_list( & heap->texture_objects );
         driSetTextureSwapCounterLocation( heap, NULL );
+
+        heap->weight = heap->size;
+        heap->duty = 0;
       }
       else {
         FREE( heap );
@@ -884,7 +946,7 @@ get_max_size( unsigned nr_heaps,
     do { if ( max_sizes[v] != 0 ) { limits-> f = max_sizes[v]; } } while( 0 )
 
 #define SET_MAX_RECT(f,v) \
-    do { if ( max_sizes[v] != 0 ) { limits-> f = 1 << max_sizes[v]; } } while( 0 )
+    do { if ( max_sizes[v] != 0 ) { limits-> f = 1 << (max_sizes[v] - 1); } } while( 0 )
 
 
 /**
@@ -910,19 +972,22 @@ get_max_size( unsigned nr_heaps,
  *     For hardware that does not support mipmapping, this will be 1.
  * \param all_textures_one_heap True if the hardware requires that all
  *     textures be in a single texture heap for multitexturing.
+ * \param allow_larger_textures 0 conservative, 1 calculate limits
+ *     so at least one worst-case texture can fit, 2 just use hw limits.
  */
 
 void
 driCalculateMaxTextureLevels( driTexHeap * const * heaps,
                              unsigned nr_heaps,
                              struct gl_constants * limits,
-                             unsigned max_bytes_per_texel, 
+                             unsigned max_bytes_per_texel,
                              unsigned max_2D_size,
                              unsigned max_3D_size,
                              unsigned max_cube_size,
                              unsigned max_rect_size,
                              unsigned mipmaps_at_once,
-                             int all_textures_one_heap )
+                             int all_textures_one_heap,
+                             int allow_larger_textures )
 {
    struct maps_per_heap  max_textures[8];
    unsigned         i;
@@ -939,8 +1004,8 @@ driCalculateMaxTextureLevels( driTexHeap * const * heaps,
 
    mipmaps[0] = mipmaps_at_once;
    mipmaps[1] = mipmaps_at_once;
-   mipmaps[2] = 1;
-   mipmaps[3] = mipmaps_at_once;
+   mipmaps[2] = mipmaps_at_once;
+   mipmaps[3] = 1;
 
 
    /* Calculate the maximum number of texture levels in two passes.  The
@@ -951,18 +1016,22 @@ driCalculateMaxTextureLevels( driTexHeap * const * heaps,
     */
 
    for ( i = 0 ; i < 4 ; i++ ) {
-      if ( max_sizes[ i ] != 0 ) {
-        fill_in_maximums( heaps, nr_heaps, max_bytes_per_texel, 
+      if ( (allow_larger_textures != 2) && (max_sizes[ i ] != 0) ) {
+        fill_in_maximums( heaps, nr_heaps, max_bytes_per_texel,
                           max_sizes[ i ], mipmaps[ i ],
                           dimensions[ i ], faces[ i ],
                           max_textures );
 
-        max_sizes[ i ] = get_max_size( nr_heaps, 
-                                       limits->MaxTextureUnits,
+        max_sizes[ i ] = get_max_size( nr_heaps,
+                                       allow_larger_textures == 1 ?
+                                       1 : limits->MaxTextureUnits,
                                        max_sizes[ i ],
                                        all_textures_one_heap,
                                        max_textures );
       }
+      else if (max_sizes[ i ] != 0) {
+        max_sizes[ i ] += 1;
+      }
    }
 
    SET_MAX( MaxTextureLevels,        0 );
@@ -1077,7 +1146,7 @@ driValidateTextureHeaps( driTexHeap * const * texture_heaps,
       unsigned textures_in_heap = 0;
       unsigned blocks_in_mempool = 0;
       const driTexHeap * heap = texture_heaps[i];
-      const memHeap_t * p = heap->memory_heap;
+      const struct mem_block *p = heap->memory_heap;
 
       /* Check each texture object has a MemBlock, and is linked into
        * the correct heap.