summaryrefslogtreecommitdiffstats
path: root/src/mesa/drivers/dri/mga/mgatexmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mesa/drivers/dri/mga/mgatexmem.c')
-rw-r--r--src/mesa/drivers/dri/mga/mgatexmem.c564
1 files changed, 564 insertions, 0 deletions
diff --git a/src/mesa/drivers/dri/mga/mgatexmem.c b/src/mesa/drivers/dri/mga/mgatexmem.c
new file mode 100644
index 00000000000..7dbdbc582fa
--- /dev/null
+++ b/src/mesa/drivers/dri/mga/mgatexmem.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright 2000-2001 VA Linux Systems, Inc.
+ * 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
+ * VA LINUX SYSTEMS AND/OR ITS 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:
+ * Keith Whitwell <[email protected]>
+ */
+/* $XFree86: xc/lib/GL/mesa/src/drv/mga/mgatexmem.c,v 1.7 2002/10/30 12:51:36 alanh Exp $ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <GL/gl.h>
+
+#include "mm.h"
+#include "mgacontext.h"
+#include "mgatex.h"
+#include "mgaregs.h"
+#include "mgaioctl.h"
+
+/*#include "mem.h" */
+#include "simple_list.h"
+
+static void
+mgaSwapOutTexObj(mgaContextPtr mmesa, mgaTextureObjectPtr t)
+{
+ if (t->MemBlock) {
+ mmFreeMem(t->MemBlock);
+ t->MemBlock = 0;
+
+ if (t->age > mmesa->dirtyAge)
+ mmesa->dirtyAge = t->age;
+ }
+
+ t->dirty_images = ~0;
+ move_to_tail(&(mmesa->SwappedOut), t);
+}
+
+static void
+mgaPrintLocalLRU( mgaContextPtr mmesa, int heap )
+{
+ mgaTextureObjectPtr t;
+ int sz = 1 << (mmesa->mgaScreen->logTextureGranularity[heap]);
+
+ fprintf(stderr, "\nLocal LRU, heap %d:\n", heap);
+
+ foreach( t, &(mmesa->TexObjList[heap]) ) {
+ if (!t->tObj)
+ fprintf(stderr, "Placeholder %d at %x sz %x\n",
+ t->MemBlock->ofs / sz,
+ t->MemBlock->ofs,
+ t->MemBlock->size);
+ else
+ fprintf(stderr, "Texture (bound %d) at %x sz %x\n",
+ t->bound,
+ t->MemBlock->ofs,
+ t->MemBlock->size);
+ }
+
+ fprintf(stderr, "\n\n");
+}
+
+static void
+mgaPrintGlobalLRU( mgaContextPtr mmesa, int heap )
+{
+ int i, j;
+ drmTextureRegion *list = mmesa->sarea->texList[heap];
+
+ fprintf(stderr, "\nGlobal LRU, heap %d list %p:\n", heap, list);
+
+ for (i = 0, j = MGA_NR_TEX_REGIONS ; i < MGA_NR_TEX_REGIONS ; i++) {
+ fprintf(stderr, "list[%d] age %d next %d prev %d\n",
+ j, list[j].age, list[j].next, list[j].prev);
+ j = list[j].next;
+ if (j == MGA_NR_TEX_REGIONS) break;
+ }
+
+ if (j != MGA_NR_TEX_REGIONS) {
+ fprintf(stderr, "Loop detected in global LRU\n\n\n");
+ for (i = 0 ; i < MGA_NR_TEX_REGIONS ; i++) {
+ fprintf(stderr, "list[%d] age %d next %d prev %d\n",
+ i, list[i].age, list[i].next, list[i].prev);
+ }
+ }
+
+ fprintf(stderr, "\n\n");
+}
+
+
+static void mgaResetGlobalLRU( mgaContextPtr mmesa, GLuint heap )
+{
+ drmTextureRegion *list = mmesa->sarea->texList[heap];
+ int sz = 1 << mmesa->mgaScreen->logTextureGranularity[heap];
+ int i;
+
+ mmesa->texAge[heap] = ++mmesa->sarea->texAge[heap];
+
+ if (0) fprintf(stderr, "mgaResetGlobalLRU %d\n", (int)heap);
+
+ /* (Re)initialize the global circular LRU list. The last element
+ * in the array (MGA_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->mgaScreen->textureSize[heap] ; i++) {
+ list[i].prev = i-1;
+ list[i].next = i+1;
+ list[i].age = mmesa->sarea->texAge[heap];
+ }
+
+ i--;
+ list[0].prev = MGA_NR_TEX_REGIONS;
+ list[i].prev = i-1;
+ list[i].next = MGA_NR_TEX_REGIONS;
+ list[MGA_NR_TEX_REGIONS].prev = i;
+ list[MGA_NR_TEX_REGIONS].next = 0;
+
+}
+
+
+static void mgaUpdateTexLRU( mgaContextPtr mmesa, mgaTextureObjectPtr t )
+{
+ int i;
+ int heap = t->heap;
+ int logsz = mmesa->mgaScreen->logTextureGranularity[heap];
+ int start = t->MemBlock->ofs >> logsz;
+ int end = (t->MemBlock->ofs + t->MemBlock->size - 1) >> logsz;
+ drmTextureRegion *list = mmesa->sarea->texList[heap];
+
+ mmesa->texAge[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 );
+
+
+ if (0)
+ fprintf(stderr, "mgaUpdateTexLRU heap %d list %p\n", heap, list);
+
+
+ /* Update the global LRU
+ */
+ for (i = start ; i <= end ; i++) {
+
+ list[i].in_use = 1;
+ list[i].age = mmesa->texAge[heap];
+
+ /* remove_from_list(i)
+ */
+ list[(unsigned)list[i].next].prev = list[i].prev;
+ list[(unsigned)list[i].prev].next = list[i].next;
+
+ /* insert_at_head(list, i)
+ */
+ list[i].prev = MGA_NR_TEX_REGIONS;
+ list[i].next = list[MGA_NR_TEX_REGIONS].next;
+ list[(unsigned)list[MGA_NR_TEX_REGIONS].next].prev = i;
+ list[MGA_NR_TEX_REGIONS].next = i;
+ }
+
+ if (0) {
+ mgaPrintGlobalLRU(mmesa, t->heap);
+ mgaPrintLocalLRU(mmesa, t->heap);
+ }
+}
+
+/* Called for every shared texture region which has increased in age
+ * since we last held the lock.
+ *
+ * Figures out which of our textures have been ejected by other clients,
+ * and pushes a placeholder texture onto the LRU list to represent
+ * the other client's textures.
+ */
+static void mgaTexturesGone( mgaContextPtr mmesa,
+ GLuint heap,
+ GLuint offset,
+ GLuint size,
+ GLuint in_use )
+{
+ mgaTextureObjectPtr 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 off. Need to hold onto the currently bound
+ * objects, however.
+ */
+ if (t->bound)
+ mgaSwapOutTexObj( mmesa, t );
+ else
+ mgaDestroyTexObj( mmesa, t );
+ }
+
+
+ if (in_use) {
+ t = (mgaTextureObjectPtr) calloc(1, sizeof(*t));
+ if (!t) return;
+
+ t->heap = heap;
+ 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 );
+ }
+}
+
+
+void mgaAgeTextures( mgaContextPtr mmesa, int heap )
+{
+ MGASAREAPrivPtr sarea = mmesa->sarea;
+ int sz = 1 << (mmesa->mgaScreen->logTextureGranularity[heap]);
+ int idx, nr = 0;
+
+ /* 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][MGA_NR_TEX_REGIONS].prev ;
+ idx != MGA_NR_TEX_REGIONS && nr < MGA_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->mgaScreen->textureSize[heap] ) {
+ nr = MGA_NR_TEX_REGIONS;
+ break;
+ }
+
+ if (sarea->texList[heap][idx].age > mmesa->texAge[heap]) {
+ mgaTexturesGone(mmesa, heap, idx * sz, sz,
+ sarea->texList[heap][idx].in_use);
+ }
+ }
+
+ if (nr == MGA_NR_TEX_REGIONS) {
+ mgaTexturesGone(mmesa, heap, 0,
+ mmesa->mgaScreen->textureSize[heap], 0);
+ mgaResetGlobalLRU( mmesa, heap );
+ }
+
+
+ if (0) {
+ mgaPrintGlobalLRU( mmesa, heap );
+ mgaPrintLocalLRU( mmesa, heap );
+ }
+
+ mmesa->texAge[heap] = sarea->texAge[heap];
+ mmesa->dirty |= MGA_UPLOAD_TEX0IMAGE | MGA_UPLOAD_TEX1IMAGE;
+}
+
+/*
+ * mgaUploadSubImageLocked
+ *
+ * Perform an iload based update of a resident buffer. This is used for
+ * both initial loading of the entire image, and texSubImage updates.
+ *
+ * Performed with the hardware lock held.
+ */
+void mgaUploadSubImageLocked( mgaContextPtr mmesa,
+ mgaTextureObjectPtr t,
+ int level,
+ int x, int y, int width, int height )
+{
+ int x2;
+ int dwords;
+ int offset;
+ struct gl_texture_image *image;
+ int texelBytes, texelsPerDword, texelMaccess, length;
+
+ if ( level < 0 || level >= MGA_TEX_MAXLEVELS )
+ return;
+
+ image = t->tObj->Image[level];
+ if ( !image ) return;
+
+
+ if (image->Data == 0) {
+ fprintf(stderr, "null texture image data tObj %p level %d\n",
+ t->tObj, level);
+ return;
+ }
+
+
+ /* find the proper destination offset for this level */
+ offset = (t->MemBlock->ofs +
+ t->offsets[level]);
+
+
+ texelBytes = t->texelBytes;
+ switch( texelBytes ) {
+ case 1:
+ texelsPerDword = 4;
+ texelMaccess = 0;
+ break;
+ case 2:
+ texelsPerDword = 2;
+ texelMaccess = 1;
+ break;
+ case 4:
+ texelsPerDword = 1;
+ texelMaccess = 2;
+ break;
+ default:
+ return;
+ }
+
+
+ /* We can't do a subimage update if pitch is < 32 texels due
+ * to hardware XY addressing limits, so we will need to
+ * linearly upload all modified rows.
+ */
+ if ( image->Width < 32 ) {
+ x = 0;
+ width = image->Width * height;
+ height = 1;
+
+ /* Assume that 1x1 textures aren't going to cause a
+ * bus error if we read up to four texels from that
+ * location:
+ */
+/* if ( width < texelsPerDword ) { */
+/* width = texelsPerDword; */
+/* } */
+ } else {
+ /* pad the size out to dwords. The image is a pointer
+ to the entire image, so we can safely reference
+ outside the x,y,width,height bounds if we need to */
+ x2 = x + width;
+ x2 = (x2 + (texelsPerDword-1)) & ~(texelsPerDword-1);
+ x = (x + (texelsPerDword-1)) & ~(texelsPerDword-1);
+ width = x2 - x;
+ }
+
+ /* we may not be able to upload the entire texture in one
+ batch due to register limits or dma buffer limits.
+ Recursively split it up. */
+ while ( 1 ) {
+ dwords = height * width / texelsPerDword;
+ if ( dwords * 4 <= MGA_BUFFER_SIZE ) {
+ break;
+ }
+
+ mgaUploadSubImageLocked( mmesa, t, level, x, y,
+ width, height >> 1 );
+ y += ( height >> 1 );
+ height -= ( height >> 1 );
+ }
+
+ length = dwords * 4;
+
+ /* Fill in the secondary buffer with properly converted texels
+ * from the mesa buffer. */
+ /* FIXME: the sync for direct copy reduces speed.. */
+ if(t->heap == MGA_CARD_HEAP ) {
+ mgaGetILoadBufferLocked( mmesa );
+ mgaConvertTexture( (GLuint *)mmesa->iload_buffer->address,
+ texelBytes, image, x, y, width, height );
+ if(length < 64) length = 64;
+
+ if (0)
+ fprintf(stderr, "TexelBytes : %d, offset: %d, length : %d\n",
+ texelBytes,
+ mmesa->mgaScreen->textureOffset[t->heap] +
+ offset +
+ y * width * 4/texelsPerDword,
+ length);
+
+ mgaFireILoadLocked( mmesa,
+ mmesa->mgaScreen->textureOffset[t->heap] +
+ offset +
+ y * width * 4/texelsPerDword,
+ length);
+ } else {
+ /* This works, is slower for uploads to card space and needs
+ * additional synchronization with the dma stream.
+ */
+
+ UPDATE_LOCK(mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT);
+ mgaConvertTexture( (GLuint *)
+ (mmesa->mgaScreen->texVirtual[t->heap] +
+ offset +
+ y * width * 4/texelsPerDword),
+ texelBytes, image, x, y, width, height );
+ }
+}
+
+
+static void mgaUploadTexLevel( mgaContextPtr mmesa,
+ mgaTextureObjectPtr t,
+ int l )
+{
+ mgaUploadSubImageLocked( mmesa,
+ t,
+ l,
+ 0, 0,
+ t->tObj->Image[l]->Width,
+ t->tObj->Image[l]->Height);
+}
+
+
+
+
+#if 0
+static void mgaMigrateTexture( mgaContextPtr mmesa, mgaTextureObjectPtr t )
+{
+ /* NOT DONE */
+}
+#endif
+
+
+static int mgaChooseTexHeap( mgaContextPtr mmesa, mgaTextureObjectPtr t )
+{
+ int freeagp, freecard;
+ int fitincard, fitinagp;
+ int totalcard, totalagp;
+ TMemBlock *b;
+
+ totalcard = totalagp = fitincard = fitinagp = freeagp = freecard = 0;
+
+ b = mmesa->texHeap[0];
+ while(b)
+ {
+ totalcard += b->size;
+ if(b->free) if(t->totalSize <= b->size)fitincard = 1;
+ b = b->next;
+ }
+
+ b = mmesa->texHeap[1];
+ while(b)
+ {
+ totalagp += b->size;
+ if(b->free) if(t->totalSize <= b->size)fitinagp = 1;
+ b = b->next;
+ }
+
+ if(fitincard)return 0;
+ if(fitinagp)return 1;
+
+ if(totalcard && totalagp)
+ {
+ int ages;
+ int ratio = (totalcard > totalagp) ? totalcard / totalagp : totalagp / totalcard;
+ ages = mmesa->sarea->texAge[0] + mmesa->sarea->texAge[1];
+ if( (ages % ratio) == 0)return totalcard > totalagp ? 1 : 0;
+ else return totalcard > totalagp ? 0 : 1;
+ }
+
+ if(totalagp) return 1;
+ return 0;
+}
+
+
+int mgaUploadTexImages( mgaContextPtr mmesa, mgaTextureObjectPtr t )
+{
+ int heap;
+ int i;
+ int ofs;
+
+ heap = t->heap = mgaChooseTexHeap( mmesa, t );
+
+ /* Do we need to eject LRU texture objects?
+ */
+ if (!t->MemBlock) {
+ while (1)
+ {
+ mgaTextureObjectPtr tmp = mmesa->TexObjList[heap].prev;
+
+ t->MemBlock = mmAllocMem( mmesa->texHeap[heap],
+ t->totalSize,
+ 6, 0 );
+ if (t->MemBlock)
+ break;
+
+ if (mmesa->TexObjList[heap].prev->bound) {
+ fprintf(stderr, "Hit bound texture in upload\n");
+ return -1;
+ }
+
+ if (mmesa->TexObjList[heap].prev ==
+ &(mmesa->TexObjList[heap]))
+ {
+ fprintf(stderr, "Failed to upload texture, sz %d\n", t->totalSize);
+ mmDumpMemInfo( mmesa->texHeap[heap] );
+ return -1;
+ }
+
+ mgaDestroyTexObj( mmesa, tmp );
+ }
+
+ ofs = t->MemBlock->ofs
+ + mmesa->mgaScreen->textureOffset[heap]
+ ;
+
+ t->setup.texorg = ofs;
+ t->setup.texorg1 = ofs + t->offsets[1];
+ t->setup.texorg2 = ofs + t->offsets[2];
+ t->setup.texorg3 = ofs + t->offsets[3];
+ t->setup.texorg4 = ofs + t->offsets[4];
+
+ mmesa->dirty |= MGA_UPLOAD_CONTEXT;
+ }
+
+ /* Let the world know we've used this memory recently.
+ */
+ mgaUpdateTexLRU( mmesa, t );
+
+
+ if (MGA_DEBUG&DEBUG_VERBOSE_LRU)
+ fprintf(stderr, "dispatch age: %d age freed memory: %d\n",
+ GET_DISPATCH_AGE(mmesa), mmesa->dirtyAge);
+
+ if (mmesa->dirtyAge >= GET_DISPATCH_AGE(mmesa))
+ mgaWaitAgeLocked( mmesa, mmesa->dirtyAge );
+
+ if (t->dirty_images) {
+ if (MGA_DEBUG&DEBUG_VERBOSE_LRU)
+ fprintf(stderr, "*");
+
+ for (i = 0 ; i <= t->lastLevel ; i++)
+ if (t->dirty_images & (1<<i))
+ mgaUploadTexLevel( mmesa, t, i );
+ }
+
+
+ t->dirty_images = 0;
+ return 0;
+}