blob: 506b5c4c8f63a6069ba83adacf355a739a06050a [file] [log] [blame]
/* $XFree86: xc/lib/GL/mesa/src/drv/gamma/gamma_texmem.c,v 1.5 2002/11/05 17:46:07 tsi Exp $ */
#include <stdlib.h>
#include <stdio.h>
#include "glheader.h"
#include "colormac.h"
#include "macros.h"
#include "mtypes.h"
#include "simple_list.h"
#include "enums.h"
#include "mm.h"
#include "glint_dri.h"
#include "gamma_context.h"
#include "gamma_lock.h"
void gammaDestroyTexObj(gammaContextPtr gmesa, gammaTextureObjectPtr t)
{
if (!t) return;
/* 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 (gmesa && t->age > gmesa->dirtyAge)
gmesa->dirtyAge = t->age;
}
if (t->globj)
t->globj->DriverData = 0;
if (gmesa) {
if (gmesa->CurrentTexObj[0] == t) {
gmesa->CurrentTexObj[0] = 0;
gmesa->dirty &= ~GAMMA_UPLOAD_TEX0;
}
#if 0
if (gmesa->CurrentTexObj[1] == t) {
gmesa->CurrentTexObj[1] = 0;
gmesa->dirty &= ~GAMMA_UPLOAD_TEX1;
}
#endif
}
remove_from_list(t);
free(t);
}
void gammaSwapOutTexObj(gammaContextPtr gmesa, gammaTextureObjectPtr t)
{
/* fprintf(stderr, "%s\n", __FUNCTION__); */
if (t->MemBlock) {
mmFreeMem(t->MemBlock);
t->MemBlock = 0;
if (t->age > gmesa->dirtyAge)
gmesa->dirtyAge = t->age;
}
t->dirty_images = ~0;
move_to_tail(&(gmesa->SwappedOut), t);
}
/* Upload an image from mesa's internal copy.
*/
static void gammaUploadTexLevel( gammaContextPtr gmesa, gammaTextureObjectPtr t, int level )
{
const struct gl_texture_image *image = t->image[level].image;
int i,j;
int l2d;
#if 0
int offset = 0;
#endif
int words, depthLog2;
/* fprintf(stderr, "%s\n", __FUNCTION__); */
l2d = 5; /* 32bits per texel == 1<<5 */
if (level == 0) {
t->TextureAddressMode &= ~(TAM_WidthMask | TAM_HeightMask);
t->TextureAddressMode |= (image->WidthLog2 << 9) |
(image->HeightLog2 << 13);
t->TextureReadMode &= ~(TRM_WidthMask | TRM_HeightMask |
TRM_DepthMask | TRM_Border |
TRM_Patch);
t->TextureReadMode |= (image->WidthLog2 << 1) |
(image->HeightLog2 << 5) |
(l2d << 9);
t->TextureFormat &= ~(TF_CompnentsMask | TF_OneCompFmt_Mask);
}
t->TextureBaseAddr[level] = /* ??? */
(unsigned long)(t->image[level].offset + t->BufAddr) << 5;
CALC_LOG2(depthLog2, 1<<l2d);
words = (image->Width * image->Height) >> (5-depthLog2);
CHECK_DMA_BUFFER(gmesa, 3);
WRITE(gmesa->buf, LBWindowBase, t->TextureBaseAddr[level] >> 5);
WRITE(gmesa->buf, TextureCacheControl, (TCC_Enable | TCC_Invalidate));
WRITE(gmesa->buf, WaitForCompletion, 0);
FLUSH_DMA_BUFFER(gmesa);
switch (t->image[level].internalFormat) {
case GL_RGB:
case 3:
{
GLubyte *src = (GLubyte *)image->Data;
if (level == 0)
t->TextureFormat |= TF_Compnents_3;
#if 0 /* This is the texture download code we SHOULD be using */
/* In the routines below, but this causes an DMA overrun - WHY ? */
while (offset < words) {
int count = gmesa->bufSize;
int i;
count -= 3;
if (count > words-offset) count = words-offset;
gmesa->buf->i = GlintTextureDownloadOffsetTag;
gmesa->buf++;
gmesa->buf->i = offset;
gmesa->buf++;
gmesa->buf->i = (GlintTextureDataTag | ((count-1) << 16));
gmesa->buf++;
for (i = 0; i < count; i++) {
gmesa->buf->i = PACK_COLOR_565(src[0],src[1],src[2]);
gmesa->buf++;
src += 3;
}
gmesa->bufCount = count+3; /* texture data + 3 values */
offset += count;
FLUSH_DMA_BUFFER(gmesa);
}
#else
/* The UGLY way, and SLOW !, but the above sometimes causes
* a DMA overrun error ??? FIXME ! */
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureDownloadOffset, 0);
for (i = 0; i < words; i++) {
unsigned int data;
data = PACK_COLOR_565(src[0],src[1],src[2]);
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureData, data);
src += 3;
}
FLUSH_DMA_BUFFER(gmesa);
#endif
}
break;
case GL_RGBA:
case 4:
{
GLubyte *src = (GLubyte *)image->Data;
if (level == 0)
t->TextureFormat |= TF_Compnents_4;
/* The UGLY way, and SLOW !, but the above sometimes causes
* a DMA overrun error ??? FIXME ! */
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureDownloadOffset, 0);
for (i = 0; i < words; i++) {
unsigned int data;
data = PACK_COLOR_8888(src[0],src[1],src[2],src[3]);
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureData, data);
src += 4;
}
FLUSH_DMA_BUFFER(gmesa);
}
break;
case GL_LUMINANCE:
{
GLubyte *src = (GLubyte *)image->Data;
if (level == 0)
t->TextureFormat |= TF_Compnents_1 | TF_OneCompFmt_Lum;
/* The UGLY way, and SLOW !, but the above sometimes causes
* a DMA overrun error ??? FIXME ! */
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureDownloadOffset, 0);
for (i = 0; i < words; i++) {
unsigned int data;
data = PACK_COLOR_888(src[0],src[0],src[0]);
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureData, data);
src ++;
}
FLUSH_DMA_BUFFER(gmesa);
}
break;
case GL_INTENSITY:
{
GLubyte *src = (GLubyte *)image->Data;
if (level == 0)
t->TextureFormat |= TF_Compnents_1 | TF_OneCompFmt_Intensity;
/* The UGLY way, and SLOW !, but the above sometimes causes
* a DMA overrun error ??? FIXME ! */
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureDownloadOffset, 0);
for (i = 0; i < words; i++) {
unsigned int data;
data = PACK_COLOR_8888(src[0],src[0],src[0],src[0]);
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureData, data);
src ++;
}
FLUSH_DMA_BUFFER(gmesa);
}
break;
case GL_LUMINANCE_ALPHA:
{
GLubyte *src = (GLubyte *)image->Data;
if (level == 0)
t->TextureFormat |= TF_Compnents_2;
/* The UGLY way, and SLOW !, but the above sometimes causes
* a DMA overrun error ??? FIXME ! */
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureDownloadOffset, 0);
for (i = 0; i < words; i++) {
unsigned int data;
data = PACK_COLOR_8888(src[0],src[0],src[0],src[1]);
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureData, data);
src += 2;
}
FLUSH_DMA_BUFFER(gmesa);
}
break;
case GL_ALPHA:
{
GLubyte *src = (GLubyte *)image->Data;
if (level == 0)
t->TextureFormat |= TF_Compnents_1 | TF_OneCompFmt_Alpha;
/* The UGLY way, and SLOW !, but the above sometimes causes
* a DMA overrun error ??? FIXME ! */
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureDownloadOffset, 0);
for (i = 0; i < words; i++) {
unsigned int data;
data = PACK_COLOR_8888(255,255,255,src[0]);
CHECK_DMA_BUFFER(gmesa, 1);
WRITE(gmesa->buf, TextureData, data);
src += 1;
}
FLUSH_DMA_BUFFER(gmesa);
}
break;
/* TODO: Translate color indices *now*:
*/
case GL_COLOR_INDEX:
{
GLubyte *dst = (GLubyte *)(t->BufAddr + t->image[level].offset);
GLubyte *src = (GLubyte *)image->Data;
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->Format));
}
CHECK_DMA_BUFFER(gmesa, 2);
WRITE(gmesa->buf, WaitForCompletion, 0);
WRITE(gmesa->buf, LBWindowBase, gmesa->LBWindowBase);
}
void gammaPrintLocalLRU( gammaContextPtr gmesa )
{
gammaTextureObjectPtr t;
int sz = 1 << (gmesa->gammaScreen->logTextureGranularity);
foreach( t, &gmesa->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 gammaPrintGlobalLRU( gammaContextPtr gmesa )
{
int i, j;
GAMMATextureRegionPtr list = gmesa->sarea->texList;
for (i = 0, j = GAMMA_NR_TEX_REGIONS ; i < GAMMA_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 == GAMMA_NR_TEX_REGIONS) break;
}
if (j != GAMMA_NR_TEX_REGIONS)
fprintf(stderr, "Loop detected in global LRU\n");
}
void gammaResetGlobalLRU( gammaContextPtr gmesa )
{
GAMMATextureRegionPtr list = gmesa->sarea->texList;
int sz = 1 << gmesa->gammaScreen->logTextureGranularity;
int i;
/* (Re)initialize the global circular LRU list. The last element
* in the array (GAMMA_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 <= gmesa->gammaScreen->textureSize ; i++) {
list[i].prev = i-1;
list[i].next = i+1;
list[i].age = 0;
}
i--;
list[0].prev = GAMMA_NR_TEX_REGIONS;
list[i].prev = i-1;
list[i].next = GAMMA_NR_TEX_REGIONS;
list[GAMMA_NR_TEX_REGIONS].prev = i;
list[GAMMA_NR_TEX_REGIONS].next = 0;
gmesa->sarea->texAge = 0;
}
void gammaUpdateTexLRU( gammaContextPtr gmesa, gammaTextureObjectPtr t )
{
int i;
int logsz = gmesa->gammaScreen->logTextureGranularity;
int start = t->MemBlock->ofs >> logsz;
int end = (t->MemBlock->ofs + t->MemBlock->size - 1) >> logsz;
GAMMATextureRegionPtr list = gmesa->sarea->texList;
gmesa->texAge = ++gmesa->sarea->texAge;
/* Update our local LRU
*/
move_to_head( &(gmesa->TexObjList), t );
/* Update the global LRU
*/
for (i = start ; i <= end ; i++) {
list[i].in_use = 1;
list[i].age = gmesa->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 = GAMMA_NR_TEX_REGIONS;
list[i].next = list[GAMMA_NR_TEX_REGIONS].next;
list[(unsigned)list[GAMMA_NR_TEX_REGIONS].next].prev = i;
list[GAMMA_NR_TEX_REGIONS].next = i;
}
}
/* 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 gammaTexturesGone( gammaContextPtr gmesa,
GLuint offset,
GLuint size,
GLuint in_use )
{
gammaTextureObjectPtr t, tmp;
foreach_s ( t, tmp, &gmesa->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.
*/
gammaSwapOutTexObj( gmesa, t );
}
if (in_use) {
t = (gammaTextureObjectPtr) calloc(1,sizeof(*t));
if (!t) return;
t->MemBlock = mmAllocMem( gmesa->texHeap, size, 0, offset);
insert_at_head( &gmesa->TexObjList, t );
}
/* Reload any lost textures referenced by current vertex buffer.
*/
#if 0
if (gmesa->vertex_buffer) {
int i, j;
fprintf(stderr, "\n\nreload tex\n");
for (i = 0 ; i < gmesa->statenr ; i++) {
for (j = 0 ; j < 2 ; j++) {
gammaTextureObjectPtr t = gmesa->state_tex[j][i];
if (t) {
if (t->MemBlock == 0)
gammaUploadTexImages( gmesa, t );
}
}
}
/* Hard to do this with the lock held:
*/
/* GAMMA_FIREVERTICES( gmesa ); */
}
#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 gammaUploadTexImages( gammaContextPtr gmesa, gammaTextureObjectPtr t )
{
int i;
int ofs;
int numLevels;
/* /fprintf(stderr, "%s\n", __FUNCTION__); */
#if 0
LOCK_HARDWARE( gmesa );
#endif
/* Do we need to eject LRU texture objects?
*/
if (!t->MemBlock) {
while (1)
{
t->MemBlock = mmAllocMem( gmesa->texHeap, t->totalSize, 12, 0 );
if (t->MemBlock)
break;
if (gmesa->TexObjList.prev == gmesa->CurrentTexObj[0] ||
gmesa->TexObjList.prev == gmesa->CurrentTexObj[1]) {
fprintf(stderr, "Hit bound texture in upload\n");
gammaPrintLocalLRU( gmesa );
return;
}
if (gmesa->TexObjList.prev == &(gmesa->TexObjList)) {
fprintf(stderr, "Failed to upload texture, sz %d\n", t->totalSize);
mmDumpMemInfo( gmesa->texHeap );
return;
}
gammaSwapOutTexObj( gmesa, gmesa->TexObjList.prev );
}
ofs = t->MemBlock->ofs;
t->BufAddr = (char *)(unsigned long)(gmesa->LBWindowBase + ofs); /* ??? */
if (t == gmesa->CurrentTexObj[0])
gmesa->dirty |= GAMMA_UPLOAD_TEX0;
#if 0
if (t == gmesa->CurrentTexObj[1])
gmesa->dirty |= GAMMA_UPLOAD_TEX1;
#endif
gammaUpdateTexLRU( gmesa, t );
}
#if 0
if (gmesa->dirtyAge >= GET_DISPATCH_AGE(gmesa))
gammaWaitAgeLocked( gmesa, gmesa->dirtyAge );
#endif
numLevels = t->lastLevel - t->firstLevel + 1;
for (i = 0 ; i < numLevels ; i++)
if (t->dirty_images & (1<<i))
gammaUploadTexLevel( gmesa, t, i );
t->dirty_images = 0;
#if 0
UNLOCK_HARDWARE( gmesa );
#endif
}