diff options
Diffstat (limited to 'src/mesa/drivers/dri/mach64/mach64_texmem.c')
-rw-r--r-- | src/mesa/drivers/dri/mach64/mach64_texmem.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/src/mesa/drivers/dri/mach64/mach64_texmem.c b/src/mesa/drivers/dri/mach64/mach64_texmem.c new file mode 100644 index 00000000000..44dd888a968 --- /dev/null +++ b/src/mesa/drivers/dri/mach64/mach64_texmem.c @@ -0,0 +1,867 @@ +/* $XFree86$ */ /* -*- mode: c; c-basic-offset: 3 -*- */ +/* + * Copyright 1999, 2000 ATI Technologies Inc. and Precision Insight, Inc., + * Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * ATI, PRECISION INSIGHT AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Authors: + * Gareth Hughes <[email protected]> + * Leif Delgass <[email protected]> + * Jos� Fonseca <[email protected]> + */ + +#include "mach64_context.h" +#include "mach64_state.h" +#include "mach64_ioctl.h" +#include "mach64_vb.h" +#include "mach64_tris.h" +#include "mach64_tex.h" + +#include "context.h" +#include "macros.h" +#include "simple_list.h" +#include "texformat.h" +#include "imports.h" + + +/* Destroy hardware state associated with texture `t'. + */ +void mach64DestroyTexObj( mach64ContextPtr mmesa, mach64TexObjPtr t ) +{ +#if ENABLE_PERF_BOXES + /* Bump the performace counter */ + if (mmesa) + mmesa->c_textureSwaps++; +#endif + if ( !t ) return; + +#if 0 + if ( t->tObj && t->memBlock && mmesa ) { + /* not a placeholder, so release from global LRU if necessary */ + int heap = t->heap; + drmTextureRegion *list = mmesa->sarea->texList[heap]; + int log2sz = mmesa->mach64Screen->logTexGranularity[heap]; + int start = t->memBlock->ofs >> log2sz; + int end = (t->memBlock->ofs + t->memBlock->size - 1) >> log2sz; + int i; + + mmesa->lastTexAge[heap] = ++mmesa->sarea->texAge[heap]; + + /* Update the global LRU */ + for ( i = start ; i <= end ; i++ ) { + /* do we own this block? */ + if (list[i].in_use == mmesa->hHWContext) { + list[i].in_use = 0; + list[i].age = mmesa->lastTexAge[heap]; + + /* remove_from_list(i) */ + list[(GLuint)list[i].next].prev = list[i].prev; + list[(GLuint)list[i].prev].next = list[i].next; + } + } + } +#endif + + if ( t->memBlock ) { + mmFreeMem( t->memBlock ); + t->memBlock = NULL; + } + + if ( t->tObj ) { + t->tObj->DriverData = NULL; + } + + if ( t->bound && mmesa ) + mmesa->CurrentTexObj[t->bound-1] = NULL; + + remove_from_list( t ); + FREE( t ); +} + +/* Keep track of swapped out texture objects. + */ +void mach64SwapOutTexObj( mach64ContextPtr mmesa, + mach64TexObjPtr t ) +{ +#if ENABLE_PERF_BOXES + /* Bump the performace counter */ + if (mmesa) + mmesa->c_textureSwaps++; +#endif + +#if 0 + if ( t->tObj && t->memBlock && mmesa ) { + /* not a placeholder, so release from global LRU if necessary */ + int heap = t->heap; + drmTextureRegion *list = mmesa->sarea->texList[heap]; + int log2sz = mmesa->mach64Screen->logTexGranularity[heap]; + int start = t->memBlock->ofs >> log2sz; + int end = (t->memBlock->ofs + t->memBlock->size - 1) >> log2sz; + int i; + + mmesa->lastTexAge[heap] = ++mmesa->sarea->texAge[heap]; + + /* Update the global LRU */ + for ( i = start ; i <= end ; i++ ) { + /* do we own this block? */ + if (list[i].in_use == mmesa->hHWContext) { + list[i].in_use = 0; + list[i].age = mmesa->lastTexAge[heap]; + + /* remove_from_list(i) */ + list[(GLuint)list[i].next].prev = list[i].prev; + list[(GLuint)list[i].prev].next = list[i].next; + } + } + } +#endif + + if ( t->memBlock ) { + mmFreeMem( t->memBlock ); + t->memBlock = NULL; + } + + t->dirty = ~0; + move_to_tail( &mmesa->SwappedOut, t ); +} + +/* Print out debugging information about texture LRU. + */ +void mach64PrintLocalLRU( mach64ContextPtr mmesa, int heap ) +{ + mach64TexObjPtr t; + int sz = 1 << (mmesa->mach64Screen->logTexGranularity[heap]); + + fprintf( stderr, "\nLocal LRU, heap %d:\n", heap ); + + foreach( t, &mmesa->TexObjList[heap] ) { + if ( !t->tObj ) { + fprintf( stderr, "Placeholder %d at 0x%x sz 0x%x\n", + t->memBlock->ofs / sz, + t->memBlock->ofs, + t->memBlock->size ); + } else { + fprintf( stderr, "Texture (bound %d) at 0x%x sz 0x%x\n", + t->bound, + t->memBlock->ofs, + t->memBlock->size ); + } + } + + fprintf( stderr, "\n" ); +} + +void mach64PrintGlobalLRU( mach64ContextPtr mmesa, int heap ) +{ + drmTextureRegion *list = mmesa->sarea->texList[heap]; + int i, j; + + fprintf( stderr, "\nGlobal LRU, heap %d list %p:\n", heap, list ); + + for ( i = 0, j = MACH64_NR_TEX_REGIONS ; i < MACH64_NR_TEX_REGIONS ; i++ ) { + fprintf( stderr, "list[%d] age %d in_use %d next %d prev %d\n", + j, list[j].age, list[j].in_use, list[j].next, list[j].prev ); + j = list[j].next; + if ( j == MACH64_NR_TEX_REGIONS ) break; + } + + if ( j != MACH64_NR_TEX_REGIONS ) { + fprintf( stderr, "Loop detected in global LRU\n" ); + for ( i = 0 ; i < MACH64_NR_TEX_REGIONS ; i++ ) { + fprintf( stderr, "list[%d] age %d in_use %d next %d prev %d\n", + i, list[i].age, list[i].in_use, list[i].next, list[i].prev ); + } + } + + fprintf( stderr, "\n" ); +} + +/* Reset the global texture LRU. + */ +/* NOTE: This function is only called while holding the hardware lock */ +static void mach64ResetGlobalLRU( mach64ContextPtr mmesa, int heap ) +{ + drmTextureRegion *list = mmesa->sarea->texList[heap]; + int sz = 1 << mmesa->mach64Screen->logTexGranularity[heap]; + int i; + + /* (Re)initialize the global circular LRU list. The last element in + * the array (MACH64_NR_TEX_REGIONS) is the sentinal. Keeping it at + * the end of the array allows it to be addressed rationally when + * looking up objects at a particular location in texture memory. + */ + for ( i = 0 ; (i+1) * sz <= mmesa->mach64Screen->texSize[heap] ; i++ ) { + list[i].prev = i-1; + list[i].next = i+1; + list[i].age = 0; + list[i].in_use = 0; + } + + i--; + list[0].prev = MACH64_NR_TEX_REGIONS; + list[i].prev = i-1; + list[i].next = MACH64_NR_TEX_REGIONS; + list[MACH64_NR_TEX_REGIONS].prev = i; + list[MACH64_NR_TEX_REGIONS].next = 0; + mmesa->sarea->texAge[heap] = 0; +} + +/* Update the local and global texture LRUs. + */ +/* NOTE: This function is only called while holding the hardware lock */ +void mach64UpdateTexLRU( mach64ContextPtr mmesa, + mach64TexObjPtr t ) +{ + int heap = t->heap; + drmTextureRegion *list = mmesa->sarea->texList[heap]; + int log2sz = mmesa->mach64Screen->logTexGranularity[heap]; + int start = t->memBlock->ofs >> log2sz; + int end = (t->memBlock->ofs + t->memBlock->size - 1) >> log2sz; + int i; + + mmesa->lastTexAge[heap] = ++mmesa->sarea->texAge[heap]; + + if ( !t->memBlock ) { + fprintf( stderr, "no memblock\n\n" ); + return; + } + + /* Update our local LRU */ + move_to_head( &mmesa->TexObjList[heap], t ); + + /* Update the global LRU */ + for ( i = start ; i <= end ; i++ ) { + list[i].in_use = mmesa->hHWContext; + list[i].age = mmesa->lastTexAge[heap]; + +#if 0 + /* if this is the last region, it's not in the list */ + if ( !(i*(1<<log2sz) > mmesa->mach64Screen->texSize[heap] ) ) { +#endif + /* remove_from_list(i) */ + list[(GLuint)list[i].next].prev = list[i].prev; + list[(GLuint)list[i].prev].next = list[i].next; +#if 0 + } +#endif + + /* insert_at_head(list, i) */ + list[i].prev = MACH64_NR_TEX_REGIONS; + list[i].next = list[MACH64_NR_TEX_REGIONS].next; + list[(GLuint)list[MACH64_NR_TEX_REGIONS].next].prev = i; + list[MACH64_NR_TEX_REGIONS].next = i; + } + + if ( MACH64_DEBUG & DEBUG_VERBOSE_LRU ) { + mach64PrintGlobalLRU( mmesa, t->heap ); + mach64PrintLocalLRU( mmesa, t->heap ); + } +} + +/* Update our notion of what textures have been changed since we last + * held the lock. This pertains to both our local textures and the + * textures belonging to other clients. Keep track of other client's + * textures by pushing a placeholder texture onto the LRU list -- these + * are denoted by (tObj == NULL). + */ +/* NOTE: This function is only called while holding the hardware lock */ +static void mach64TexturesGone( mach64ContextPtr mmesa, int heap, + int offset, int size, int in_use ) +{ + mach64TexObjPtr t, tmp; + + foreach_s ( t, tmp, &mmesa->TexObjList[heap] ) { + if ( t->memBlock->ofs >= offset + size || + t->memBlock->ofs + t->memBlock->size <= offset ) + continue; + + /* It overlaps - kick it out. Need to hold onto the currently + * bound objects, however. + */ + if ( t->bound ) { + mach64SwapOutTexObj( mmesa, t ); + } else { + mach64DestroyTexObj( mmesa, t ); + } + } + + if ( in_use > 0 && in_use != mmesa->hHWContext ) { + t = (mach64TexObjPtr) CALLOC( sizeof(*t) ); + if (!t) return; + + t->memBlock = mmAllocMem( mmesa->texHeap[heap], size, 0, offset ); + if ( !t->memBlock ) { + fprintf( stderr, "Couldn't alloc placeholder sz %x ofs %x\n", + (int)size, (int)offset ); + mmDumpMemInfo( mmesa->texHeap[heap] ); + return; + } + insert_at_head( &mmesa->TexObjList[heap], t ); + } +} + +/* Update our client's shared texture state. If another client has + * modified a region in which we have textures, then we need to figure + * out which of our textures has been removed, and update our global + * LRU. + */ +void mach64AgeTextures( mach64ContextPtr mmesa, int heap ) +{ + ATISAREAPrivPtr sarea = mmesa->sarea; + + if ( sarea->texAge[heap] != mmesa->lastTexAge[heap] ) { + int sz = 1 << mmesa->mach64Screen->logTexGranularity[heap]; + int nr = 0; + int idx; + + /* Have to go right round from the back to ensure stuff ends up + * LRU in our local list... Fix with a cursor pointer. + */ + for ( idx = sarea->texList[heap][MACH64_NR_TEX_REGIONS].prev ; + idx != MACH64_NR_TEX_REGIONS && nr < MACH64_NR_TEX_REGIONS ; + idx = sarea->texList[heap][idx].prev, nr++ ) + { + /* If switching texturing schemes, then the SAREA might not + * have been properly cleared, so we need to reset the + * global texture LRU. + */ + if ( idx * sz > mmesa->mach64Screen->texSize[heap] ) { + nr = MACH64_NR_TEX_REGIONS; + break; + } + + if ( sarea->texList[heap][idx].age > mmesa->lastTexAge[heap] ) { + mach64TexturesGone( mmesa, heap, idx * sz, sz, + sarea->texList[heap][idx].in_use ); + } + } + + /* If switching texturing schemes, then the SAREA might not + * have been properly cleared, so we need to reset the + * global texture LRU. + */ + if ( nr == MACH64_NR_TEX_REGIONS ) { + mach64TexturesGone( mmesa, heap, 0, + mmesa->mach64Screen->texSize[heap], 0 ); + mach64ResetGlobalLRU( mmesa, heap ); + } + + if ( 0 ) { + mach64PrintGlobalLRU( mmesa, heap ); + mach64PrintLocalLRU( mmesa, heap ); + } + + mmesa->dirty |= (MACH64_UPLOAD_CONTEXT | + MACH64_UPLOAD_TEX0IMAGE | + MACH64_UPLOAD_TEX1IMAGE); + mmesa->lastTexAge[heap] = sarea->texAge[heap]; + } +} + +/* Upload the texture image associated with texture `t' at level `level' + * at the address relative to `start'. + */ +static void mach64UploadAGPSubImage( mach64ContextPtr mmesa, + mach64TexObjPtr t, int level, + int x, int y, int width, int height ) +{ + mach64ScreenRec *mach64Screen = mmesa->mach64Screen; + struct gl_texture_image *image; + int texelsPerDword = 0; + int dwords; + + /* Ensure we have a valid texture to upload */ + if ( ( level < 0 ) || ( level > mmesa->glCtx->Const.MaxTextureLevels ) ) + return; + + image = t->tObj->Image[level]; + if ( !image ) + return; + + switch ( image->TexFormat->TexelBytes ) { + case 1: texelsPerDword = 4; break; + case 2: texelsPerDword = 2; break; + case 4: texelsPerDword = 1; break; + } + +#if 1 + /* FIXME: The subimage index calcs are wrong... */ + x = 0; + y = 0; + width = image->Width; + height = image->Height; +#endif + + dwords = width * height / texelsPerDword; + +#if ENABLE_PERF_BOXES + /* Bump the performance counter */ + mmesa->c_agpTextureBytes += (dwords << 2); +#endif + + if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) { + fprintf( stderr, "mach64UploadSubImage: %d,%d of %d,%d at %d,%d\n", + width, height, image->Width, image->Height, x, y ); + fprintf( stderr, " blit ofs: 0x%07x pitch: 0x%x dwords: %d\n", + (GLuint)t->offset, (GLint)width, dwords ); + mmDumpMemInfo( mmesa->texHeap[t->heap] ); + } + + assert(image->Data); + + { + CARD32 *dst = (CARD32 *)((char *)mach64Screen->agpTextures.map + t->memBlock->ofs); + const GLubyte *src = (const GLubyte *) image->Data + + (y * image->Width + x) * image->TexFormat->TexelBytes; + const GLuint bytes = width * height * image->TexFormat->TexelBytes; + memcpy(dst, src, bytes); + } + +} + +/* Upload the texture image associated with texture `t' at level `level' + * at the address relative to `start'. + */ +static void mach64UploadLocalSubImage( mach64ContextPtr mmesa, + mach64TexObjPtr t, int level, + int x, int y, int width, int height ) +{ + struct gl_texture_image *image; + int texelsPerDword = 0; + int imageWidth, imageHeight; + int remaining, rows; + int format, dwords; + const int maxdwords = (MACH64_BUFFER_MAX_DWORDS - (MACH64_HOSTDATA_BLIT_OFFSET / 4)); + CARD32 pitch, offset; + int i; + + /* Ensure we have a valid texture to upload */ + if ( ( level < 0 ) || ( level > mmesa->glCtx->Const.MaxTextureLevels ) ) + return; + + image = t->tObj->Image[level]; + if ( !image ) + return; + + switch ( image->TexFormat->TexelBytes ) { + case 1: texelsPerDword = 4; break; + case 2: texelsPerDword = 2; break; + case 4: texelsPerDword = 1; break; + } + +#if 1 + /* FIXME: The subimage index calcs are wrong... */ + x = 0; + y = 0; + width = image->Width; + height = image->Height; +#endif + + imageWidth = image->Width; + imageHeight = image->Height; + + format = t->textureFormat; + + /* The texel upload routines have a minimum width, so force the size + * if needed. + */ + if ( imageWidth < texelsPerDword ) { + int factor; + + factor = texelsPerDword / imageWidth; + imageWidth = texelsPerDword; + imageHeight /= factor; + if ( imageHeight == 0 ) { + /* In this case, the texel converter will actually walk a + * texel or two off the end of the image, but normal malloc + * alignment should prevent it from ever causing a fault. + */ + imageHeight = 1; + } + } + + /* We can't upload to a pitch less than 64 texels so we will need to + * linearly upload all modified rows for textures smaller than this. + * This makes the x/y/width/height different for the blitter and the + * texture walker. + */ + if ( imageWidth >= 64 ) { + /* The texture walker and the blitter look identical */ + pitch = imageWidth >> 3; + } else { + int factor; + int y2; + int start, end; + + start = (y * imageWidth) & ~63; + end = (y + height) * imageWidth; + + if ( end - start < 64 ) { + /* Handle the case where the total number of texels + * uploaded is < 64. + */ + x = 0; + y = start / 64; + width = end - start; + height = 1; + } else { + /* Upload some number of full 64 texel blit rows */ + factor = 64 / imageWidth; + + y2 = y + height - 1; + y /= factor; + y2 /= factor; + + x = 0; + width = 64; + height = y2 - y + 1; + } + + /* Fixed pitch of 64 */ + pitch = 8; + } + + dwords = width * height / texelsPerDword; + offset = t->offset; + +#if ENABLE_PERF_BOXES + /* Bump the performance counter */ + mmesa->c_textureBytes += (dwords << 2); +#endif + + if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) { + fprintf( stderr, "mach64UploadSubImage: %d,%d of %d,%d at %d,%d\n", + width, height, image->Width, image->Height, x, y ); + fprintf( stderr, " blit ofs: 0x%07x pitch: 0x%x dwords: %d\n", + (GLuint)offset, (GLint)width, dwords ); + mmDumpMemInfo( mmesa->texHeap[t->heap] ); + } + + /* Subdivide the texture if required (account for the registers added by the drm) */ + if ( dwords <= maxdwords ) { + rows = height; + } else { + rows = (maxdwords * texelsPerDword) / (2 * width); + } + + for ( i = 0, remaining = height ; + remaining > 0 ; + remaining -= rows, y += rows, i++ ) + { + drmBufPtr buffer; + CARD32 *dst; + + height = MIN2(remaining, rows); + + /* Grab the dma buffer for the texture blit */ + buffer = mach64GetBufferLocked( mmesa ); + + dst = (CARD32 *)((char *)buffer->address + MACH64_HOSTDATA_BLIT_OFFSET); + + assert(image->Data); + + { + const GLubyte *src = (const GLubyte *) image->Data + + (y * image->Width + x) * image->TexFormat->TexelBytes; + const GLuint bytes = width * height * image->TexFormat->TexelBytes; + memcpy(dst, src, bytes); + } + + mach64FireBlitLocked( mmesa, buffer, offset, pitch, format, + x, y, width, height ); + + } + + mmesa->new_state |= MACH64_NEW_CONTEXT; + mmesa->dirty |= MACH64_UPLOAD_CONTEXT | MACH64_UPLOAD_MISC; +} + + +/* Upload the texture images associated with texture `t'. This might + * require removing our own and/or other client's texture objects to + * make room for these images. + */ +void mach64UploadTexImages( mach64ContextPtr mmesa, mach64TexObjPtr t ) +{ + GLint heap; + + if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) { + fprintf( stderr, "%s( %p, %p )\n", + __FUNCTION__, mmesa->glCtx, t ); + } + + assert(t); + assert(t->tObj); + + /* Choose the heap appropriately */ + heap = MACH64_CARD_HEAP; + + if ( !mmesa->mach64Screen->IsPCI && + t->size > mmesa->mach64Screen->texSize[heap] ) { + heap = MACH64_AGP_HEAP; + } + + /* Do we need to eject LRU texture objects? */ + if ( !t->memBlock ) { + t->heap = heap; + + /* Allocate a memory block on a 64-byte boundary */ + t->memBlock = mmAllocMem( mmesa->texHeap[heap], t->size, 6, 0 ); + + /* Try AGP before kicking anything out of local mem */ + if ( !mmesa->mach64Screen->IsPCI && !t->memBlock && heap == MACH64_CARD_HEAP ) { + t->memBlock = mmAllocMem( mmesa->texHeap[MACH64_AGP_HEAP], + t->size, 6, 0 ); + + if ( t->memBlock ) + heap = t->heap = MACH64_AGP_HEAP; + } + + /* Kick out textures until the requested texture fits */ + while ( !t->memBlock ) { + if ( mmesa->TexObjList[heap].prev->bound ) { + fprintf( stderr, + "mach64UploadTexImages: ran into bound texture\n" ); + return; + } + if ( mmesa->TexObjList[heap].prev == &mmesa->TexObjList[heap] ) { + if ( mmesa->mach64Screen->IsPCI ) { + fprintf( stderr, "%s: upload texture failure on " + "local texture heaps, sz=%d\n", __FUNCTION__, + t->size ); + return; + } else if ( heap == MACH64_CARD_HEAP ) { + heap = t->heap = MACH64_AGP_HEAP; + continue; + } else { + int i; + fprintf( stderr, "%s: upload texture failure on " + "%sAGP texture heaps, sz=%d\n", __FUNCTION__, + mmesa->firstTexHeap == MACH64_CARD_HEAP ? "both local and " : "", + t->size ); + for ( i = mmesa->firstTexHeap ; i < mmesa->lastTexHeap ; i++ ) { + mach64PrintLocalLRU( mmesa, i ); + mmDumpMemInfo( mmesa->texHeap[i] ); + } + exit(-1); + return; + } + } + + mach64SwapOutTexObj( mmesa, mmesa->TexObjList[heap].prev ); + + t->memBlock = mmAllocMem( mmesa->texHeap[heap], t->size, 6, 0 ); + } + + /* Set the base offset of the texture image */ + t->offset = mmesa->mach64Screen->texOffset[heap] + t->memBlock->ofs; + + /* Force loading the new state into the hardware */ + mmesa->dirty |= (MACH64_UPLOAD_SCALE_3D_CNTL | + MACH64_UPLOAD_TEXTURE); + } + + /* Let the world know we've used this memory recently */ + mach64UpdateTexLRU( mmesa, t ); + + /* Upload any images that are new */ + if ( t->dirty ) { + if (t->heap == MACH64_AGP_HEAP) { + /* Need to make sure any vertex buffers in the queue complete */ + mach64WaitForIdleLocked( mmesa ); + mach64UploadAGPSubImage( mmesa, t, t->tObj->BaseLevel, 0, 0, + t->tObj->Image[0][t->tObj->BaseLevel]->Width, + t->tObj->Image[0][t->tObj->BaseLevel]->Height ); + } else { + mach64UploadLocalSubImage( mmesa, t, t->tObj->BaseLevel, 0, 0, + t->tObj->Image[0][t->tObj->BaseLevel]->Width, + t->tObj->Image[0][t->tObj->BaseLevel]->Height ); + } + + mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH; + } + + mmesa->dirty |= MACH64_UPLOAD_TEXTURE; + + t->dirty = 0; +} + +/* The mach64 needs to have both primary and secondary textures in either + * local or AGP memory, so we need a "buddy system" to make sure that allocation + * succeeds or fails for both textures. + * FIXME: This needs to be optimized better. + */ +void mach64UploadMultiTexImages( mach64ContextPtr mmesa, + mach64TexObjPtr t0, + mach64TexObjPtr t1 ) +{ + GLint heap; + + if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) { + fprintf( stderr, "%s( %p, %p %p )\n", + __FUNCTION__, mmesa->glCtx, t0, t1 ); + } + + assert(t0 && t1); + assert(t0->tObj && t1->tObj); + + /* Choose the heap appropriately */ + heap = MACH64_CARD_HEAP; + + if ( !mmesa->mach64Screen->IsPCI && + ((t0->size + t1->size) > mmesa->mach64Screen->texSize[heap]) ) { + heap = MACH64_AGP_HEAP; + } + + /* Do we need to eject LRU texture objects? */ + if ( !t0->memBlock || !t1->memBlock || t0->heap != t1->heap ) { + /* FIXME: starting from scratch for now to keep it simple */ + if ( t0->memBlock ) { + mach64SwapOutTexObj( mmesa, t0 ); + } + if ( t1->memBlock ) { + mach64SwapOutTexObj( mmesa, t1 ); + } + t0->heap = t1->heap = heap; + /* Allocate a memory block on a 64-byte boundary */ + t0->memBlock = mmAllocMem( mmesa->texHeap[heap], t0->size, 6, 0 ); + if ( t0->memBlock ) { + t1->memBlock = mmAllocMem( mmesa->texHeap[heap], t1->size, 6, 0 ); + if ( !t1->memBlock ) { + mmFreeMem( t0->memBlock ); + t0->memBlock = NULL; + } + } + /* Try AGP before kicking anything out of local mem */ + if ( (!t0->memBlock || !t1->memBlock) && heap == MACH64_CARD_HEAP ) { + t0->memBlock = mmAllocMem( mmesa->texHeap[MACH64_AGP_HEAP], t0->size, 6, 0 ); + if ( t0->memBlock ) { + t1->memBlock = mmAllocMem( mmesa->texHeap[MACH64_AGP_HEAP], t1->size, 6, 0 ); + if ( !t1->memBlock ) { + mmFreeMem( t0->memBlock ); + t0->memBlock = NULL; + } + } + + if ( t0->memBlock && t1->memBlock ) + heap = t0->heap = t1->heap = MACH64_AGP_HEAP; + } + + /* Kick out textures until the requested texture fits */ + while ( !t0->memBlock || !t1->memBlock ) { + if ( mmesa->TexObjList[heap].prev->bound ) { + fprintf( stderr, + "%s: ran into bound texture\n", __FUNCTION__ ); + return; + } + if ( mmesa->TexObjList[heap].prev == &mmesa->TexObjList[heap] ) { + if ( mmesa->mach64Screen->IsPCI ) { + fprintf( stderr, "%s: upload texture failure on local " + "texture heaps, tex0 sz=%d tex1 sz=%d\n", __FUNCTION__, + t0->size, t1->size ); + return; + } else if ( heap == MACH64_CARD_HEAP ) { + /* If only one allocation succeeded, start over again in AGP */ + if (t0->memBlock) { + mmFreeMem( t0->memBlock ); + t0->memBlock = NULL; + } + if (t1->memBlock) { + mmFreeMem( t1->memBlock ); + t1->memBlock = NULL; + } + heap = t0->heap = t1->heap = MACH64_AGP_HEAP; + continue; + } else { + int i; + fprintf( stderr, "%s: upload texture failure on %s" + "AGP texture heaps, tex0 sz=%d tex1 sz=%d\n", __FUNCTION__, + mmesa->firstTexHeap == MACH64_CARD_HEAP ? "both local and " : "", + t0->size, t1->size ); + for ( i = mmesa->firstTexHeap ; i < mmesa->lastTexHeap ; i++ ) { + mach64PrintLocalLRU( mmesa, i ); + mmDumpMemInfo( mmesa->texHeap[i] ); + } + exit(-1); + return; + } + } + + mach64SwapOutTexObj( mmesa, mmesa->TexObjList[heap].prev ); + + if (!t0->memBlock) + t0->memBlock = mmAllocMem( mmesa->texHeap[heap], t0->size, 6, 0 ); + if (!t1->memBlock) + t1->memBlock = mmAllocMem( mmesa->texHeap[heap], t1->size, 6, 0 ); + } + + /* Set the base offset of the texture image */ + t0->offset = mmesa->mach64Screen->texOffset[heap] + t0->memBlock->ofs; + t1->offset = mmesa->mach64Screen->texOffset[heap] + t1->memBlock->ofs; + + /* Force loading the new state into the hardware */ + mmesa->dirty |= (MACH64_UPLOAD_SCALE_3D_CNTL | + MACH64_UPLOAD_TEXTURE); + } + + /* Let the world know we've used this memory recently */ + mach64UpdateTexLRU( mmesa, t0 ); + mach64UpdateTexLRU( mmesa, t1 ); + + /* Upload any images that are new */ + if ( t0->dirty ) { + if (t0->heap == MACH64_AGP_HEAP) { + /* Need to make sure any vertex buffers in the queue complete */ + mach64WaitForIdleLocked( mmesa ); + mach64UploadAGPSubImage( mmesa, t0, t0->tObj->BaseLevel, 0, 0, + t0->tObj->Image[0][t0->tObj->BaseLevel]->Width, + t0->tObj->Image[0][t0->tObj->BaseLevel]->Height ); + } else { + mach64UploadLocalSubImage( mmesa, t0, t0->tObj->BaseLevel, 0, 0, + t0->tObj->Image[0][t0->tObj->BaseLevel]->Width, + t0->tObj->Image[0][t0->tObj->BaseLevel]->Height ); + } + mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH; + } + if ( t1->dirty ) { + if (t1->heap == MACH64_AGP_HEAP) { + /* Need to make sure any vertex buffers in the queue complete */ + mach64WaitForIdleLocked( mmesa ); + mach64UploadAGPSubImage( mmesa, t1, t1->tObj->BaseLevel, 0, 0, + t1->tObj->Image[0][t1->tObj->BaseLevel]->Width, + t1->tObj->Image[0][t1->tObj->BaseLevel]->Height ); + } else { + mach64UploadLocalSubImage( mmesa, t1, t1->tObj->BaseLevel, 0, 0, + t1->tObj->Image[0][t1->tObj->BaseLevel]->Width, + t1->tObj->Image[0][t1->tObj->BaseLevel]->Height ); + } + + mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH; + } + + mmesa->dirty |= MACH64_UPLOAD_TEXTURE; + + t0->dirty = 0; + t1->dirty = 0; +} |