| /* |
| * Author: Max Lingua <sunmax@libero.it> |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #include "glheader.h" |
| #include "macros.h" |
| #include "mtypes.h" |
| #include "simple_list.h" |
| #include "enums.h" |
| |
| #include "mm.h" |
| #include "s3v_context.h" |
| #include "s3v_lock.h" |
| #include "s3v_tex.h" |
| |
| void s3vSwapOutTexObj(s3vContextPtr vmesa, s3vTextureObjectPtr t); |
| void s3vUpdateTexLRU( s3vContextPtr vmesa, s3vTextureObjectPtr t ); |
| |
| |
| void s3vDestroyTexObj(s3vContextPtr vmesa, s3vTextureObjectPtr t) |
| { |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| DEBUG_TEX(("*** s3vDestroyTexObj: #%i ***\n", ++times)); |
| #endif |
| |
| if (!t) return; |
| |
| /* FIXME: useful? */ |
| #if _TEXFLUSH |
| if (vmesa) |
| DMAFLUSH(); |
| #endif |
| |
| /* This is sad - need to sync *in case* we upload a texture |
| * to this newly free memory... |
| */ |
| if (t->MemBlock) { |
| mmFreeMem(t->MemBlock); |
| t->MemBlock = 0; |
| |
| if (vmesa && t->age > vmesa->dirtyAge) |
| vmesa->dirtyAge = t->age; |
| } |
| |
| if (t->globj) |
| t->globj->DriverData = NULL; |
| |
| if (vmesa) { |
| if (vmesa->CurrentTexObj[0] == t) { |
| vmesa->CurrentTexObj[0] = 0; |
| vmesa->dirty &= ~S3V_UPLOAD_TEX0; |
| } |
| |
| #if 0 |
| if (vmesa->CurrentTexObj[1] == t) { |
| vmesa->CurrentTexObj[1] = 0; |
| vmesa->dirty &= ~S3V_UPLOAD_TEX1; |
| } |
| #endif |
| } |
| |
| remove_from_list(t); |
| FREE(t); |
| } |
| |
| |
| void s3vSwapOutTexObj(s3vContextPtr vmesa, s3vTextureObjectPtr t) |
| { |
| /* int i; */ |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| DEBUG_TEX(("*** s3vSwapOutTexObj: #%i ***\n", ++times)); |
| #endif |
| |
| if (t->MemBlock) { |
| |
| mmFreeMem(t->MemBlock); |
| t->MemBlock = 0; |
| |
| if (t->age > vmesa->dirtyAge) |
| vmesa->dirtyAge = t->age; |
| |
| t->dirty_images = ~0; |
| move_to_tail(&(vmesa->SwappedOut), t); |
| } |
| } |
| |
| |
| /* Upload an image from mesa's internal copy. |
| */ |
| |
| static void s3vUploadTexLevel( s3vContextPtr vmesa, s3vTextureObjectPtr t, |
| int level ) |
| { |
| __DRIscreenPrivate *sPriv = vmesa->driScreen; |
| const struct gl_texture_image *image = t->image[level].image; |
| int i,j; |
| int l2d; |
| /* int offset = 0; */ |
| int words; |
| GLuint* dest; |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| #endif |
| if ( !image ) return; |
| if (image->Data == 0) return; |
| |
| DEBUG_TEX(("*** s3vUploadTexLevel: #%i ***\n", ++times)); |
| DEBUG_TEX(("level = %i\n", level)); |
| |
| l2d = 5; /* 32bits per texel == 1<<5 */ |
| /* |
| if (level == 0) |
| ; |
| */ |
| DEBUG_TEX(("t->image[%i].offset = 0x%x\n", |
| level, t->image[level].offset)); |
| |
| t->TextureBaseAddr[level] = (GLuint)(t->BufAddr + t->image[level].offset |
| + _TEXALIGN) & (GLuint)(~_TEXALIGN); |
| dest = (GLuint*)(sPriv->pFB + t->TextureBaseAddr[level]); |
| |
| DEBUG_TEX(("sPriv->pFB = 0x%x\n", sPriv->pFB)); |
| DEBUG_TEX(("dest = 0x%x\n", dest)); |
| DEBUG_TEX(("dest - sPriv->pFB = 0x%x\n", ((int)dest - (int)sPriv->pFB))); |
| |
| /* NOTE: we implicitly suppose t->texelBytes == 2 */ |
| |
| words = (image->Width * image->Height) >> 1; |
| |
| DEBUG_TEX(("\n\n")); |
| |
| switch (t->image[level].internalFormat) { |
| case GL_RGB: |
| case 3: |
| { |
| GLubyte *src = (GLubyte *)image->Data; |
| |
| DEBUG_TEX(("GL_RGB:\n")); |
| /* |
| if (level == 0) |
| ; |
| */ |
| /* The UGLY way, and SLOW : use DMA FIXME ! */ |
| |
| for (i = 0; i < words; i++) { |
| unsigned int data; |
| /* data = PACK_COLOR_565(src[0],src[1],src[2]); */ |
| data = S3VIRGEPACKCOLOR555(src[0],src[1],src[2],255) |
| |(S3VIRGEPACKCOLOR555(src[3],src[4],src[5],255)<<16); |
| |
| *dest++ = data; |
| /* src += 3; */ |
| src +=6; |
| } |
| } |
| break; |
| |
| case GL_RGBA: |
| case 4: |
| { |
| GLubyte *src = (GLubyte *)image->Data; |
| |
| DEBUG_TEX(("GL_RGBA:\n")); |
| /* |
| if (level == 0) |
| ; |
| */ |
| for (i = 0; i < words; i++) { |
| unsigned int data; |
| |
| /* data = PACK_COLOR_8888(src[0],src[1],src[2],src[3]); */ |
| data = S3VIRGEPACKCOLOR4444(src[0], src[1],src[2], src[3]) |
| | (S3VIRGEPACKCOLOR4444(src[4], src[5], src[6], src[7]) << 16); |
| |
| *dest++ = data; |
| /* src += 4; */ |
| src += 8; |
| } |
| } |
| break; |
| |
| case GL_LUMINANCE: |
| { |
| GLubyte *src = (GLubyte *)image->Data; |
| |
| DEBUG_TEX(("GL_LUMINANCE:\n")); |
| /* |
| if (level == 0) |
| ; |
| */ |
| for (i = 0; i < words; i++) { |
| unsigned int data; |
| |
| /* data = PACK_COLOR_888(src[0],src[0],src[0]); */ |
| data = S3VIRGEPACKCOLOR4444(src[0],src[0],src[0],src[0]) |
| | (S3VIRGEPACKCOLOR4444(src[1],src[1],src[1],src[1]) << 16); |
| |
| *dest++ = data; |
| /* src ++; */ |
| src +=2; |
| } |
| } |
| break; |
| |
| case GL_INTENSITY: |
| { |
| GLubyte *src = (GLubyte *)image->Data; |
| |
| DEBUG_TEX(("GL_INTENSITY:\n")); |
| /* |
| if (level == 0) |
| ; |
| */ |
| for (i = 0; i < words; i++) { |
| unsigned int data; |
| |
| /* data = PACK_COLOR_8888(src[0],src[0],src[0],src[0]); */ |
| data = S3VIRGEPACKCOLOR4444(src[0],src[0],src[0],src[0]) |
| | (S3VIRGEPACKCOLOR4444(src[1],src[1],src[1],src[1]) << 16); |
| |
| *dest++ = data; |
| /* src ++; */ |
| src += 2; |
| } |
| } |
| break; |
| |
| case GL_LUMINANCE_ALPHA: |
| { |
| GLubyte *src = (GLubyte *)image->Data; |
| |
| DEBUG_TEX(("GL_LUMINANCE_ALPHA:\n")); |
| /* |
| if (level == 0) |
| ; |
| */ |
| for (i = 0; i < words; i++) { |
| unsigned int data; |
| |
| /* data = PACK_COLOR_8888(src[0],src[0],src[0],src[1]); */ |
| data = S3VIRGEPACKCOLOR4444(src[0],src[0],src[0],src[1]) |
| | (S3VIRGEPACKCOLOR4444(src[2],src[2],src[2],src[3]) << 16); |
| |
| *dest++ = data; |
| /* src += 2; */ |
| src += 4; |
| } |
| } |
| break; |
| |
| case GL_ALPHA: |
| { |
| GLubyte *src = (GLubyte *)image->Data; |
| |
| DEBUG_TEX(("GL_ALPHA:\n")); |
| /* |
| if (level == 0) |
| ; |
| */ |
| for (i = 0; i < words; i++) { |
| unsigned int data; |
| |
| /* data = PACK_COLOR_8888(255,255,255,src[0]); */ |
| data = S3VIRGEPACKCOLOR4444(255,255,255,src[0]) |
| | (S3VIRGEPACKCOLOR4444(255,255,255,src[1]) << 16); |
| |
| *dest++ = data; |
| /* src += 1; */ |
| src += 2; |
| } |
| } |
| break; |
| |
| /* TODO: Translate color indices *now*: |
| */ |
| case GL_COLOR_INDEX: |
| { |
| |
| GLubyte *dst = (GLubyte *)(t->BufAddr + t->image[level].offset); |
| GLubyte *src = (GLubyte *)image->Data; |
| |
| DEBUG_TEX(("GL_COLOR_INDEX:\n")); |
| |
| for (j = 0 ; j < image->Height ; j++, dst += t->Pitch) { |
| for (i = 0 ; i < image->Width ; i++) { |
| dst[i] = src[0]; |
| src += 1; |
| } |
| } |
| } |
| break; |
| |
| default: |
| fprintf(stderr, "Not supported texture format %s\n", |
| _mesa_lookup_enum_by_nr(image->_BaseFormat)); |
| } |
| |
| DEBUG_TEX(("words = %i\n\n", words)); |
| } |
| |
| void s3vPrintLocalLRU( s3vContextPtr vmesa ) |
| { |
| s3vTextureObjectPtr t; |
| int sz = 1 << (vmesa->s3vScreen->logTextureGranularity); |
| |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| DEBUG_TEX(("*** s3vPrintLocalLRU: #%i ***\n", ++times)); |
| #endif |
| |
| foreach( t, &vmesa->TexObjList ) { |
| if (!t->globj) |
| fprintf(stderr, "Placeholder %d at %x sz %x\n", |
| t->MemBlock->ofs / sz, |
| t->MemBlock->ofs, |
| t->MemBlock->size); |
| else |
| fprintf(stderr, "Texture at %x sz %x\n", |
| t->MemBlock->ofs, |
| t->MemBlock->size); |
| |
| } |
| } |
| |
| void s3vPrintGlobalLRU( s3vContextPtr vmesa ) |
| { |
| int i, j; |
| S3VTexRegionPtr list = vmesa->sarea->texList; |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| DEBUG_TEX(("*** s3vPrintGlobalLRU: #%i ***\n", ++times)); |
| #endif |
| |
| for (i = 0, j = S3V_NR_TEX_REGIONS ; i < S3V_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 == S3V_NR_TEX_REGIONS) break; |
| } |
| |
| if (j != S3V_NR_TEX_REGIONS) |
| fprintf(stderr, "Loop detected in global LRU\n"); |
| } |
| |
| |
| void s3vResetGlobalLRU( s3vContextPtr vmesa ) |
| { |
| S3VTexRegionPtr list = vmesa->sarea->texList; |
| int sz = 1 << vmesa->s3vScreen->logTextureGranularity; |
| int i; |
| |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| DEBUG_TEX(("*** s3vResetGlobalLRU: #%i ***\n", ++times)); |
| #endif |
| |
| /* (Re)initialize the global circular LRU list. The last element |
| * in the array (S3V_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 <= vmesa->s3vScreen->textureSize ; i++) { |
| list[i].prev = i-1; |
| list[i].next = i+1; |
| list[i].age = 0; |
| } |
| |
| i--; |
| list[0].prev = S3V_NR_TEX_REGIONS; |
| list[i].prev = i-1; |
| list[i].next = S3V_NR_TEX_REGIONS; |
| list[S3V_NR_TEX_REGIONS].prev = i; |
| list[S3V_NR_TEX_REGIONS].next = 0; |
| vmesa->sarea->texAge = 0; |
| } |
| |
| |
| void s3vUpdateTexLRU( s3vContextPtr vmesa, s3vTextureObjectPtr t ) |
| { |
| /* |
| int i; |
| int logsz = vmesa->s3vScreen->logTextureGranularity; |
| int start = t->MemBlock->ofs >> logsz; |
| int end = (t->MemBlock->ofs + t->MemBlock->size - 1) >> logsz; |
| S3VTexRegionPtr list = vmesa->sarea->texList; |
| */ |
| |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| DEBUG_TEX(("*** s3vUpdateTexLRU: #%i ***\n", ++times)); |
| #endif |
| |
| vmesa->texAge = ++vmesa->sarea->texAge; |
| |
| /* Update our local LRU |
| */ |
| move_to_head( &(vmesa->TexObjList), t ); |
| |
| /* Update the global LRU |
| */ |
| #if 0 |
| for (i = start ; i <= end ; i++) { |
| |
| list[i].in_use = 1; |
| list[i].age = vmesa->texAge; |
| |
| /* 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 = S3V_NR_TEX_REGIONS; |
| list[i].next = list[S3V_NR_TEX_REGIONS].next; |
| list[(unsigned)list[S3V_NR_TEX_REGIONS].next].prev = i; |
| list[S3V_NR_TEX_REGIONS].next = i; |
| } |
| #endif |
| } |
| |
| |
| /* 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. |
| */ |
| void s3vTexturesGone( s3vContextPtr vmesa, |
| GLuint offset, |
| GLuint size, |
| GLuint in_use ) |
| { |
| s3vTextureObjectPtr t, tmp; |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| DEBUG_TEX(("*** s3vTexturesGone: #%i ***\n", ++times)); |
| #endif |
| |
| foreach_s ( t, tmp, &vmesa->TexObjList ) { |
| |
| 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. |
| */ |
| s3vSwapOutTexObj( vmesa, t ); |
| } |
| |
| if (in_use) { |
| t = (s3vTextureObjectPtr) calloc(1,sizeof(*t)); |
| if (!t) return; |
| |
| t->MemBlock = mmAllocMem( vmesa->texHeap, size, 0, offset); |
| insert_at_head( &vmesa->TexObjList, t ); |
| } |
| |
| /* Reload any lost textures referenced by current vertex buffer. |
| */ |
| #if 0 |
| if (vmesa->vertex_buffer) { |
| int i, j; |
| |
| fprintf(stderr, "\n\nreload tex\n"); |
| |
| for (i = 0 ; i < vmesa->statenr ; i++) { |
| for (j = 0 ; j < 2 ; j++) { |
| s3vTextureObjectPtr t = vmesa->state_tex[j][i]; |
| if (t) { |
| if (t->MemBlock == 0) |
| s3vUploadTexImages( vmesa, t ); |
| } |
| } |
| } |
| |
| /* Hard to do this with the lock held: |
| */ |
| /* S3V_FIREVERTICES( vmesa ); */ |
| } |
| #endif |
| } |
| |
| |
| /* This is called with the lock held. May have to eject our own and/or |
| * other client's texture objects to make room for the upload. |
| */ |
| void s3vUploadTexImages( s3vContextPtr vmesa, s3vTextureObjectPtr t ) |
| { |
| int i; |
| int ofs; |
| int numLevels; |
| #if TEX_DEBUG_ON |
| static unsigned int times=0; |
| static unsigned int try=0; |
| |
| DEBUG_TEX(("*** s3vUploadTexImages: #%i ***\n", ++times)); |
| DEBUG_TEX(("vmesa->texHeap = 0x%x; t->totalSize = %i\n", |
| (unsigned int)vmesa->texHeap, t->totalSize)); |
| #endif |
| |
| /* Do we need to eject LRU texture objects? |
| */ |
| if (!t->MemBlock) { |
| |
| while (1) |
| { |
| /* int try = 0; */ |
| DEBUG_TEX(("trying to alloc mem for tex (try %i)\n", ++try)); |
| |
| t->MemBlock = mmAllocMem( vmesa->texHeap, t->totalSize, 12, 0 ); |
| |
| if (t->MemBlock) |
| break; |
| |
| if (vmesa->TexObjList.prev == vmesa->CurrentTexObj[0]) { |
| /* || vmesa->TexObjList.prev == vmesa->CurrentTexObj[1]) { |
| fprintf(stderr, "Hit bound texture in upload\n"); |
| s3vPrintLocalLRU( vmesa ); */ |
| return; |
| } |
| |
| if (vmesa->TexObjList.prev == &(vmesa->TexObjList)) { |
| /* fprintf(stderr, "Failed to upload texture, sz %d\n", |
| t->totalSize); |
| mmDumpMemInfo( vmesa->texHeap ); */ |
| return; |
| } |
| |
| DEBUG_TEX(("swapping out: %p\n", vmesa->TexObjList.prev)); |
| s3vSwapOutTexObj( vmesa, vmesa->TexObjList.prev ); |
| } |
| |
| ofs = t->MemBlock->ofs; |
| |
| t->BufAddr = vmesa->s3vScreen->texOffset + ofs; |
| |
| DEBUG_TEX(("ofs = 0x%x\n", ofs)); |
| DEBUG_TEX(("t->BufAddr = 0x%x\n", t->BufAddr)); |
| |
| /* FIXME: check if we need it */ |
| #if 0 |
| if (t == vmesa->CurrentTexObj[0]) { |
| vmesa->dirty |= S3V_UPLOAD_TEX0; |
| vmesa->restore_primitive = -1; |
| } |
| #endif |
| |
| #if 0 |
| if (t == vmesa->CurrentTexObj[1]) |
| vmesa->dirty |= S3V_UPLOAD_TEX1; |
| #endif |
| |
| s3vUpdateTexLRU( vmesa, t ); |
| } |
| |
| #if 0 |
| if (vmesa->dirtyAge >= GET_DISPATCH_AGE(vmesa)) |
| s3vWaitAgeLocked( vmesa, vmesa->dirtyAge ); |
| #endif |
| |
| #if _TEXLOCK |
| S3V_SIMPLE_FLUSH_LOCK(vmesa); |
| #endif |
| numLevels = t->lastLevel - t->firstLevel + 1; |
| for (i = 0 ; i < numLevels ; i++) |
| if (t->dirty_images & (1<<i)) |
| s3vUploadTexLevel( vmesa, t, i ); |
| |
| t->dirty_images = 0; |
| #if _TEXLOCK |
| S3V_SIMPLE_UNLOCK(vmesa); |
| #endif |
| } |