/*
* Copyright 2000-2001 VA Linux Systems, Inc.
- * (c) Copyright IBM Corporation 2002
+ * (C) Copyright IBM Corporation 2002, 2003
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* Kevin E. Martin <kem@users.sourceforge.net>
* Gareth Hughes <gareth@nvidia.com>
*/
-/* $XFree86:$ */
/** \file texmem.c
* Implements all of the device-independent texture memory management.
* application.
*/
+#include "main/imports.h"
+#include "main/macros.h"
+#include "main/simple_list.h"
#include "texmem.h"
-#include "simple_list.h"
-#include "imports.h"
-
-#include <assert.h>
-
static unsigned dummy_swap_counter;
* \param n Value whose \f$\log_2\f$ is to be calculated
*/
-static unsigned
-driLog2( unsigned n )
+static GLuint
+driLog2( GLuint n )
{
- unsigned log2;
-
+ GLuint log2;
for ( log2 = 1 ; n > 1 ; log2++ ) {
n >>= 1;
*/
GLboolean
-driIsTextureResident( GLcontext * ctx,
+driIsTextureResident( struct gl_context * ctx,
struct gl_texture_object * texObj )
{
driTextureObject * t;
continue;
if (!t->tObj) {
fprintf( stderr, "Placeholder (%p) %d at 0x%x sz 0x%x\n",
- t,
+ (void *)t,
t->memBlock->ofs / sz,
t->memBlock->ofs,
t->memBlock->size );
} else {
fprintf( stderr, "Texture (%p) at 0x%x sz 0x%x\n",
- t,
+ (void *)t,
t->memBlock->ofs,
t->memBlock->size );
}
}
foreach ( t, heap->swapped_objects ) {
if (!t->tObj) {
- fprintf( stderr, "Swapped Placeholder (%p)\n", t );
+ fprintf( stderr, "Swapped Placeholder (%p)\n", (void *)t );
} else {
- fprintf( stderr, "Swapped Texture (%p)\n", t );
+ fprintf( stderr, "Swapped Texture (%p)\n", (void *)t );
}
}
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, list );
+ __FUNCTION__, callername, heap->heapId, (void *)list );
for ( i = 0, j = heap->nrRegions ; i < heap->nrRegions ; i++ ) {
fprintf( stderr, "list[%d] age %d next %d prev %d in_use %d\n",
for (i = start ; i <= end ; i++) {
- list[i].in_use = 1;
list[i].age = heap->local_age;
/* remove_from_list(i)
if ( 0 ) {
fprintf( stderr, "[%s:%d] freeing %p (tObj = %p, DriverData = %p)\n",
__FILE__, __LINE__,
- t,
- (t != NULL) ? t->tObj : NULL,
- (t != NULL && t->tObj != NULL) ? t->tObj->DriverData : NULL );
+ (void *)t,
+ (void *)((t != NULL) ? t->tObj : NULL),
+ (void *)((t != NULL && t->tObj != NULL) ? t->tObj->DriverData : NULL ));
}
if ( t != NULL ) {
}
if ( 0 ) {
- fprintf( stderr, "[%s:%d] done freeing %p\n", __FILE__, __LINE__, t );
+ fprintf( stderr, "[%s:%d] done freeing %p\n", __FILE__, __LINE__, (void *)t );
}
}
* \param heap Heap whose state is to be updated
* \param offset Byte offset in the heap that has been stolen
* \param size Size, in bytes, of the stolen block
- * \param in_use Non-zero if the block is in-use by another context
+ * \param in_use Non-zero if the block is pinned/reserved by the kernel
*/
static void driTexturesGone( driTexHeap * heap, int offset, int size,
driSwapOutTextureObject( t );
}
else {
- if ( in_use &&
- offset == t->memBlock->ofs && size == t->memBlock->size ) {
- /* Matching placeholder already exists */
- return;
- } else {
- driDestroyTextureObject( t );
- }
+ driDestroyTextureObject( t );
}
}
}
- if ( in_use ) {
+ {
t = (driTextureObject *) CALLOC( heap->texture_object_size );
if ( t == NULL ) return;
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->reserved = 1;
insert_at_head( & heap->texture_objects, t );
}
}
+#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
*/
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;
+ }
}
if ( 0 )
fprintf( stderr, "%s( %u, %p, %u, %u, %u )\n",
__FUNCTION__,
- heap_id, context, size, alignmentShift, nr_regions );
+ heap_id, (void *)context, size, alignmentShift, nr_regions );
heap = (driTexHeap *) CALLOC( sizeof( driTexHeap ) );
if ( heap != NULL ) {
heap->destroy_texture_object = destroy_tex_obj;
/* Force global heap init */
- if (heap->global_age == 0)
+ if (heap->global_age[0] == 0)
heap->local_age = ~0;
else
heap->local_age = 0;
make_empty_list( & heap->texture_objects );
driSetTextureSwapCounterLocation( heap, NULL );
+
+ heap->weight = heap->size;
+ heap->duty = 0;
}
else {
FREE( heap );
if ( 0 )
- fprintf( stderr, "%s returning %p\n", __FUNCTION__, heap );
+ fprintf( stderr, "%s returning %p\n", __FUNCTION__, (void *)heap );
return heap;
}
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 )
/**
* Given the amount of texture memory, the number of texture units, and the
* maximum size of a texel, calculate the maximum texture size the driver can
- * adverteise.
+ * advertise.
*
* \param heaps Texture heaps for this card
* \param nr_heap Number of texture 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;
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
*/
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 );
* \param targets Bit-mask of value texture targets
*/
-void driInitTextureObjects( GLcontext *ctx, driTextureObject * swapped,
+void driInitTextureObjects( struct gl_context *ctx, driTextureObject * swapped,
GLuint targets )
{
struct gl_texture_object *texObj;
ctx->Texture.CurrentUnit = i;
if ( (targets & DRI_TEXMGR_DO_TEXTURE_1D) != 0 ) {
- texObj = ctx->Texture.Unit[i].Current1D;
+ texObj = ctx->Texture.Unit[i].CurrentTex[TEXTURE_1D_INDEX];
ctx->Driver.BindTexture( ctx, GL_TEXTURE_1D, texObj );
move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
}
if ( (targets & DRI_TEXMGR_DO_TEXTURE_2D) != 0 ) {
- texObj = ctx->Texture.Unit[i].Current2D;
+ texObj = ctx->Texture.Unit[i].CurrentTex[TEXTURE_2D_INDEX];
ctx->Driver.BindTexture( ctx, GL_TEXTURE_2D, texObj );
move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
}
if ( (targets & DRI_TEXMGR_DO_TEXTURE_3D) != 0 ) {
- texObj = ctx->Texture.Unit[i].Current3D;
+ texObj = ctx->Texture.Unit[i].CurrentTex[TEXTURE_3D_INDEX];
ctx->Driver.BindTexture( ctx, GL_TEXTURE_3D, texObj );
move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
}
if ( (targets & DRI_TEXMGR_DO_TEXTURE_CUBE) != 0 ) {
- texObj = ctx->Texture.Unit[i].CurrentCubeMap;
+ texObj = ctx->Texture.Unit[i].CurrentTex[TEXTURE_CUBE_INDEX];
ctx->Driver.BindTexture( ctx, GL_TEXTURE_CUBE_MAP_ARB, texObj );
move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
}
if ( (targets & DRI_TEXMGR_DO_TEXTURE_RECT) != 0 ) {
- texObj = ctx->Texture.Unit[i].CurrentRect;
+ texObj = ctx->Texture.Unit[i].CurrentTex[TEXTURE_RECT_INDEX];
ctx->Driver.BindTexture( ctx, GL_TEXTURE_RECTANGLE_NV, texObj );
move_to_tail( swapped, (driTextureObject *) texObj->DriverData );
}
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.
*
* Check the texobj base address corresponds to the MemBlock
- * range. Check the texobj size (recalculate???) fits within
+ * range. Check the texobj size (recalculate?) fits within
* the MemBlock.
*
* Count the number of texobj's using this heap.
if ( !check_in_heap( t, heap ) ) {
fprintf( stderr, "%s memory block for texture object @ %p not "
"found in heap #%d\n",
- __FUNCTION__, t, i );
+ __FUNCTION__, (void *)t, i );
return GL_FALSE;
}
if ( t->totalSize > t->memBlock->size ) {
fprintf( stderr, "%s: Memory block for texture object @ %p is "
"only %u bytes, but %u are required\n",
- __FUNCTION__, t, t->totalSize, t->memBlock->size );
+ __FUNCTION__, (void *)t, t->totalSize, t->memBlock->size );
return GL_FALSE;
}
foreach ( t, swapped ) {
if ( t->memBlock != NULL ) {
fprintf( stderr, "%s: Swapped texobj %p has non-NULL memblock %p\n",
- __FUNCTION__, t, t->memBlock );
+ __FUNCTION__, (void *)t, (void *)t->memBlock );
return GL_FALSE;
}
i++;
}
#if 0
- fprintf( stderr, "%s: swapped texture count = %u\n", i );
+ fprintf( stderr, "%s: swapped texture count = %u\n", __FUNCTION__, i );
#endif
return GL_TRUE;
}
+
+
+
+
+/****************************************************************************/
+/**
+ * Compute which mipmap levels that really need to be sent to the hardware.
+ * This depends on the base image size, GL_TEXTURE_MIN_LOD,
+ * GL_TEXTURE_MAX_LOD, GL_TEXTURE_BASE_LEVEL, and GL_TEXTURE_MAX_LEVEL.
+ */
+
+void
+driCalculateTextureFirstLastLevel( driTextureObject * t )
+{
+ struct gl_texture_object * const tObj = t->tObj;
+ const struct gl_texture_image * const baseImage =
+ tObj->Image[0][tObj->BaseLevel];
+
+ /* These must be signed values. MinLod and MaxLod can be negative numbers,
+ * and having firstLevel and lastLevel as signed prevents the need for
+ * extra sign checks.
+ */
+ int firstLevel;
+ int lastLevel;
+
+ /* Yes, this looks overly complicated, but it's all needed.
+ */
+
+ switch (tObj->Target) {
+ case GL_TEXTURE_1D:
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_3D:
+ case GL_TEXTURE_CUBE_MAP:
+ if (tObj->MinFilter == GL_NEAREST || tObj->MinFilter == GL_LINEAR) {
+ /* GL_NEAREST and GL_LINEAR only care about GL_TEXTURE_BASE_LEVEL.
+ */
+
+ firstLevel = lastLevel = tObj->BaseLevel;
+ }
+ else {
+ firstLevel = tObj->BaseLevel + (GLint)(tObj->MinLod + 0.5);
+ firstLevel = MAX2(firstLevel, tObj->BaseLevel);
+ firstLevel = MIN2(firstLevel, tObj->BaseLevel + baseImage->MaxLog2);
+ lastLevel = tObj->BaseLevel + (GLint)(tObj->MaxLod + 0.5);
+ lastLevel = MAX2(lastLevel, t->tObj->BaseLevel);
+ lastLevel = MIN2(lastLevel, t->tObj->BaseLevel + baseImage->MaxLog2);
+ lastLevel = MIN2(lastLevel, t->tObj->MaxLevel);
+ lastLevel = MAX2(firstLevel, lastLevel); /* need at least one level */
+ }
+ break;
+ case GL_TEXTURE_RECTANGLE_NV:
+ case GL_TEXTURE_4D_SGIS:
+ firstLevel = lastLevel = 0;
+ break;
+ default:
+ return;
+ }
+
+ /* save these values */
+ t->firstLevel = firstLevel;
+ t->lastLevel = lastLevel;
+}
+
+
+
+
+/**
+ * \name DRI texture formats. These vars are initialized to either the
+ * big- or little-endian Mesa formats.
+ */
+/*@{*/
+gl_format _dri_texformat_rgba8888 = MESA_FORMAT_NONE;
+gl_format _dri_texformat_argb8888 = MESA_FORMAT_NONE;
+gl_format _dri_texformat_rgb565 = MESA_FORMAT_NONE;
+gl_format _dri_texformat_argb4444 = MESA_FORMAT_NONE;
+gl_format _dri_texformat_argb1555 = MESA_FORMAT_NONE;
+gl_format _dri_texformat_al88 = MESA_FORMAT_NONE;
+gl_format _dri_texformat_a8 = MESA_FORMAT_A8;
+gl_format _dri_texformat_ci8 = MESA_FORMAT_CI8;
+gl_format _dri_texformat_i8 = MESA_FORMAT_I8;
+gl_format _dri_texformat_l8 = MESA_FORMAT_L8;
+/*@}*/
+
+
+/**
+ * Initialize _dri_texformat_* vars according to whether we're on
+ * a big or little endian system.
+ */
+void
+driInitTextureFormats(void)
+{
+ if (_mesa_little_endian()) {
+ _dri_texformat_rgba8888 = MESA_FORMAT_RGBA8888;
+ _dri_texformat_argb8888 = MESA_FORMAT_ARGB8888;
+ _dri_texformat_rgb565 = MESA_FORMAT_RGB565;
+ _dri_texformat_argb4444 = MESA_FORMAT_ARGB4444;
+ _dri_texformat_argb1555 = MESA_FORMAT_ARGB1555;
+ _dri_texformat_al88 = MESA_FORMAT_AL88;
+ }
+ else {
+ _dri_texformat_rgba8888 = MESA_FORMAT_RGBA8888_REV;
+ _dri_texformat_argb8888 = MESA_FORMAT_ARGB8888_REV;
+ _dri_texformat_rgb565 = MESA_FORMAT_RGB565_REV;
+ _dri_texformat_argb4444 = MESA_FORMAT_ARGB4444_REV;
+ _dri_texformat_argb1555 = MESA_FORMAT_ARGB1555_REV;
+ _dri_texformat_al88 = MESA_FORMAT_AL88_REV;
+ }
+}