X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Fgles2n64%2Fsrc%2FTextures.cpp;fp=source%2Fgles2n64%2Fsrc%2FTextures.cpp;h=d39f32d49464b4a50473052246286e957e5430bc;hb=34cf40586ac07c54d9bfc5be30f28743232b6d67;hp=0000000000000000000000000000000000000000;hpb=22726e4d55be26faa48b57b22689cbedde27ae44;p=mupen64plus-pandora.git diff --git a/source/gles2n64/src/Textures.cpp b/source/gles2n64/src/Textures.cpp new file mode 100644 index 0000000..d39f32d --- /dev/null +++ b/source/gles2n64/src/Textures.cpp @@ -0,0 +1,1334 @@ +#include +#include +#include + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#include "Common.h" +#include "Config.h" +#include "OpenGL.h" +#include "Textures.h" +#include "GBI.h" +#include "RSP.h" +#include "gDP.h" +#include "gSP.h" +#include "N64.h" +#include "CRC.h" +#include "convert.h" +#include "2xSAI.h" +//#include "FrameBuffer.h" + +#define FORMAT_NONE 0 +#define FORMAT_I8 1 +#define FORMAT_IA88 2 +#define FORMAT_RGBA4444 3 +#define FORMAT_RGBA5551 4 +#define FORMAT_RGBA8888 5 + +//#define PRINT_TEXTUREFORMAT + +TextureCache cache; + +typedef u32 (*GetTexelFunc)( void *src, u16 x, u16 i, u8 palette ); + +u32 GetNone( void *src, u16 x, u16 i, u8 palette ) +{ + return 0x00000000; +} + +u32 GetCI4IA_RGBA4444( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + if (x & 1) + return IA88_RGBA4444( *(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)] ); + else + return IA88_RGBA4444( *(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)] ); +} + +u32 GetCI4IA_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + if (x & 1) + return IA88_RGBA8888( *(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)] ); + else + return IA88_RGBA8888( *(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)] ); +} + +u32 GetCI4RGBA_RGBA5551( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + if (x & 1) + return RGBA5551_RGBA5551( *(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)] ); + else + return RGBA5551_RGBA5551( *(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)] ); +} + +u32 GetCI4RGBA_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + if (x & 1) + return RGBA5551_RGBA8888( *(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)] ); + else + return RGBA5551_RGBA8888( *(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)] ); +} + +u32 GetIA31_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + return IA31_RGBA8888( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) ); +} + +u32 GetIA31_RGBA4444( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + return IA31_RGBA4444( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) ); +} + +u32 GetIA31_IA88( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + return IA31_IA88( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) ); +} + +u32 GetI4_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + return I4_RGBA8888( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) ); +} + +u32 GetI4_RGBA4444( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + return I4_RGBA4444( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) ); +} + +u32 GetI4_I8( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + return I4_I8( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) ); +} + + +u32 GetI4_IA88( void *src, u16 x, u16 i, u8 palette ) +{ + u8 color4B = ((u8*)src)[(x>>1)^(i<<1)]; + return I4_IA88( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) ); +} + +u32 GetCI8IA_RGBA4444( void *src, u16 x, u16 i, u8 palette ) +{ + return IA88_RGBA4444( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] ); +} + +u32 GetCI8IA_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + return IA88_RGBA8888( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] ); +} + +u32 GetCI8RGBA_RGBA5551( void *src, u16 x, u16 i, u8 palette ) +{ + return RGBA5551_RGBA5551( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] ); +} + +u32 GetCI8RGBA_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + return RGBA5551_RGBA8888( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] ); +} + +u32 GetIA44_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + return IA44_RGBA8888(((u8*)src)[x^(i<<1)]); +} + +u32 GetIA44_RGBA4444( void *src, u16 x, u16 i, u8 palette ) +{ + return IA44_RGBA4444(((u8*)src)[x^(i<<1)]); +} + +u32 GetIA44_IA88( void *src, u16 x, u16 i, u8 palette ) +{ + return IA44_IA88(((u8*)src)[x^(i<<1)]); +} + +u32 GetI8_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + return I8_RGBA8888(((u8*)src)[x^(i<<1)]); +} + +u32 GetI8_I8( void *src, u16 x, u16 i, u8 palette ) +{ + return ((u8*)src)[x^(i<<1)]; +} + +u32 GetI8_IA88( void *src, u16 x, u16 i, u8 palette ) +{ + return I8_IA88(((u8*)src)[x^(i<<1)]); +} + +u32 GetI8_RGBA4444( void *src, u16 x, u16 i, u8 palette ) +{ + return I8_RGBA4444(((u8*)src)[x^(i<<1)]); +} + +u32 GetRGBA5551_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + return RGBA5551_RGBA8888( ((u16*)src)[x^i] ); +} + +u32 GetRGBA5551_RGBA5551( void *src, u16 x, u16 i, u8 palette ) +{ + return RGBA5551_RGBA5551( ((u16*)src)[x^i] ); +} + +u32 GetIA88_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + return IA88_RGBA8888(((u16*)src)[x^i]); +} + +u32 GetIA88_RGBA4444( void *src, u16 x, u16 i, u8 palette ) +{ + return IA88_RGBA4444(((u16*)src)[x^i]); +} + +u32 GetIA88_IA88( void *src, u16 x, u16 i, u8 palette ) +{ + return IA88_IA88(((u16*)src)[x^i]); +} + +u32 GetRGBA8888_RGBA8888( void *src, u16 x, u16 i, u8 palette ) +{ + return ((u32*)src)[x^i]; +} + +u32 GetRGBA8888_RGBA4444( void *src, u16 x, u16 i, u8 palette ) +{ + return RGBA8888_RGBA4444(((u32*)src)[x^i]); +} + + +struct TextureFormat +{ + int format; + GetTexelFunc getTexel; + int lineShift, maxTexels; +}; + + +TextureFormat textureFormatIA[4*6] = +{ + // 4-bit + { FORMAT_RGBA5551, GetCI4RGBA_RGBA5551, 4, 4096 }, // RGBA (SELECT) + { FORMAT_NONE, GetNone, 4, 8192 }, // YUV + { FORMAT_RGBA5551, GetCI4RGBA_RGBA5551, 4, 4096 }, // CI + { FORMAT_IA88, GetIA31_IA88, 4, 8192 }, // IA + { FORMAT_IA88, GetI4_IA88, 4, 8192 }, // I + { FORMAT_RGBA8888, GetCI4IA_RGBA8888, 4, 4096 }, // IA Palette + // 8-bit + { FORMAT_RGBA5551, GetCI8RGBA_RGBA5551, 3, 2048 }, // RGBA (SELECT) + { FORMAT_NONE, GetNone, 3, 4096 }, // YUV + { FORMAT_RGBA5551, GetCI8RGBA_RGBA5551, 3, 2048 }, // CI + { FORMAT_IA88, GetIA44_IA88, 3, 4096 }, // IA + { FORMAT_IA88, GetI8_IA88, 3, 4096 }, // I + { FORMAT_RGBA8888, GetCI8IA_RGBA8888, 3, 2048 }, // IA Palette + // 16-bit + { FORMAT_RGBA5551, GetRGBA5551_RGBA5551, 2, 2048 }, // RGBA + { FORMAT_NONE, GetNone, 2, 2048 }, // YUV + { FORMAT_NONE, GetNone, 2, 2048 }, // CI + { FORMAT_IA88, GetIA88_IA88, 2, 2048 }, // IA + { FORMAT_NONE, GetNone, 2, 2048 }, // I + { FORMAT_NONE, GetNone, 2, 2048 }, // IA Palette + // 32-bit + { FORMAT_RGBA8888, GetRGBA8888_RGBA8888, 2, 1024 }, // RGBA + { FORMAT_NONE, GetNone, 2, 1024 }, // YUV + { FORMAT_NONE, GetNone, 2, 1024 }, // CI + { FORMAT_NONE, GetNone, 2, 1024 }, // IA + { FORMAT_NONE, GetNone, 2, 1024 }, // I + { FORMAT_NONE, GetNone, 2, 1024 }, // IA Palette +}; + +TextureFormat textureFormatRGBA[4*6] = +{ + // 4-bit + { FORMAT_RGBA5551, GetCI4RGBA_RGBA5551, 4, 4096 }, // RGBA (SELECT) + { FORMAT_NONE, GetNone, 4, 8192 }, // YUV + { FORMAT_RGBA5551, GetCI4RGBA_RGBA5551, 4, 4096 }, // CI + { FORMAT_RGBA4444, GetIA31_RGBA4444, 4, 8192 }, // IA + { FORMAT_RGBA4444, GetI4_RGBA4444, 4, 8192 }, // I + { FORMAT_RGBA8888, GetCI4IA_RGBA8888, 4, 4096 }, // IA Palette + // 8-bit + { FORMAT_RGBA5551, GetCI8RGBA_RGBA5551, 3, 2048 }, // RGBA (SELECT) + { FORMAT_NONE, GetNone, 3, 4096 }, // YUV + { FORMAT_RGBA5551, GetCI8RGBA_RGBA5551, 3, 2048 }, // CI + { FORMAT_RGBA4444, GetIA44_RGBA4444, 3, 4096 }, // IA + { FORMAT_RGBA8888, GetI8_RGBA8888, 3, 4096 }, // I + { FORMAT_RGBA8888, GetCI8IA_RGBA8888, 3, 2048 }, // IA Palette + // 16-bit + { FORMAT_RGBA5551, GetRGBA5551_RGBA5551, 2, 2048 }, // RGBA + { FORMAT_NONE, GetNone, 2, 2048 }, // YUV + { FORMAT_NONE, GetNone, 2, 2048 }, // CI + { FORMAT_RGBA8888, GetIA88_RGBA8888, 2, 2048 }, // IA + { FORMAT_NONE, GetNone, 2, 2048 }, // I + { FORMAT_NONE, GetNone, 2, 2048 }, // IA Palette + // 32-bit + { FORMAT_RGBA8888, GetRGBA8888_RGBA8888, 2, 1024 }, // RGBA + { FORMAT_NONE, GetNone, 2, 1024 }, // YUV + { FORMAT_NONE, GetNone, 2, 1024 }, // CI + { FORMAT_NONE, GetNone, 2, 1024 }, // IA + { FORMAT_NONE, GetNone, 2, 1024 }, // I + { FORMAT_NONE, GetNone, 2, 1024 }, // IA Palette +}; + + +TextureFormat *textureFormat = textureFormatIA; + +void __texture_format_rgba(int size, int format, TextureFormat *texFormat) +{ + if (size < G_IM_SIZ_16b) + { + if (gDP.otherMode.textureLUT == G_TT_NONE) + *texFormat = textureFormat[size*6 + G_IM_FMT_I]; + else if (gDP.otherMode.textureLUT == G_TT_RGBA16) + *texFormat = textureFormat[size*6 + G_IM_FMT_CI]; + else + *texFormat = textureFormat[size*6 + G_IM_FMT_IA]; + } + else + { + *texFormat = textureFormat[size*6 + G_IM_FMT_RGBA]; + } +} + +void __texture_format_ci(int size, int format, TextureFormat *texFormat) +{ + switch(size) + { + case G_IM_SIZ_4b: + if (gDP.otherMode.textureLUT == G_TT_IA16) + *texFormat = textureFormat[G_IM_SIZ_4b*6 + G_IM_FMT_CI_IA]; + else + *texFormat = textureFormat[G_IM_SIZ_4b*6 + G_IM_FMT_CI]; + break; + + case G_IM_SIZ_8b: + if (gDP.otherMode.textureLUT == G_TT_NONE) + *texFormat = textureFormat[G_IM_SIZ_8b*6 + G_IM_FMT_I]; + else if (gDP.otherMode.textureLUT == G_TT_IA16) + *texFormat = textureFormat[G_IM_SIZ_8b*6 + G_IM_FMT_CI_IA]; + else + *texFormat = textureFormat[G_IM_SIZ_8b*6 + G_IM_FMT_CI]; + break; + + default: + *texFormat = textureFormat[size*6 + format]; + } +} + +void __texture_format(int size, int format, TextureFormat *texFormat) +{ + if (format == G_IM_FMT_RGBA) + { + __texture_format_rgba(size, format, texFormat); + } + else if (format == G_IM_FMT_YUV) + { + *texFormat = textureFormat[size*6 + G_IM_FMT_YUV]; + } + else if (format == G_IM_FMT_CI) + { + __texture_format_ci(size, format, texFormat); + } + else if (format == G_IM_FMT_IA) + { + if (gDP.otherMode.textureLUT != G_TT_NONE) + __texture_format_ci(size, format, texFormat); + else + *texFormat = textureFormat[size*6 + G_IM_FMT_IA]; + } + else if (format == G_IM_FMT_I) + { + if (gDP.otherMode.textureLUT == G_TT_NONE) + *texFormat = textureFormat[size*6 + G_IM_FMT_I]; + else + __texture_format_ci(size, format, texFormat); + } +} + + +int isTexCacheInit = 0; + +void TextureCache_Init() +{ + u32 dummyTexture[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + isTexCacheInit = 1; + cache.current[0] = NULL; + cache.current[1] = NULL; + cache.top = NULL; + cache.bottom = NULL; + cache.numCached = 0; + cache.cachedBytes = 0; + +#ifdef __HASHMAP_OPT + cache.hash.init(11); +#endif + + if (config.texture.useIA) textureFormat = textureFormatIA; + else textureFormat = textureFormatRGBA; + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glGenTextures( 32, cache.glNoiseNames ); + + srand(time(NULL)); + u8 noise[64*64*2]; + for (u32 i = 0; i < 32; i++) + { + glBindTexture( GL_TEXTURE_2D, cache.glNoiseNames[i] ); + for (u32 y = 0; y < 64; y++) + { + for (u32 x = 0; x < 64; x++) + { + u32 r = (rand()&0xFF); + noise[y*64*2+x*2] = r; + noise[y*64*2+x*2+1] = r; + } + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 64, 64, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, noise); + } + + cache.dummy = TextureCache_AddTop(); + cache.dummy->address = 0; + cache.dummy->clampS = 1; + cache.dummy->clampT = 1; + cache.dummy->clampWidth = 4; + cache.dummy->clampHeight = 4; + cache.dummy->crc = 0; + cache.dummy->format = 0; + cache.dummy->size = 0; + cache.dummy->width = 4; + cache.dummy->height = 4; + cache.dummy->realWidth = 0; + cache.dummy->realHeight = 0; + cache.dummy->maskS = 0; + cache.dummy->maskT = 0; + cache.dummy->scaleS = 0.5f; + cache.dummy->scaleT = 0.5f; + cache.dummy->shiftScaleS = 1.0f; + cache.dummy->shiftScaleT = 1.0f; + cache.dummy->textureBytes = 64; + cache.dummy->tMem = 0; + + glBindTexture( GL_TEXTURE_2D, cache.dummy->glName ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummyTexture); + + cache.cachedBytes = cache.dummy->textureBytes; + TextureCache_ActivateDummy(0); + TextureCache_ActivateDummy(1); + CRC_BuildTable(); +} + +bool TextureCache_Verify() +{ + u16 i = 0; + CachedTexture *current; + + current = cache.top; + + while (current) + { + i++; + current = current->lower; + } + if (i != cache.numCached) return false; + + i = 0; + current = cache.bottom; + while (current) + { + i++; + current = current->higher; + } + if (i != cache.numCached) return false; + + return true; +} + +void TextureCache_RemoveBottom() +{ + CachedTexture *newBottom = cache.bottom->higher; + +#ifdef __HASHMAP_OPT + CachedTexture* tex= cache.hash.find(cache.bottom->crc); + if (tex == cache.bottom) + cache.hash.insert(cache.bottom->crc, NULL); +#endif + + glDeleteTextures( 1, &cache.bottom->glName ); + cache.cachedBytes -= cache.bottom->textureBytes; + + if (cache.bottom == cache.top) + cache.top = NULL; + + free( cache.bottom ); + + cache.bottom = newBottom; + + if (cache.bottom) + cache.bottom->lower = NULL; + + cache.numCached--; +} + +void TextureCache_Remove( CachedTexture *texture ) +{ + if ((texture == cache.bottom) && (texture == cache.top)) + { + cache.top = NULL; + cache.bottom = NULL; + } + else if (texture == cache.bottom) + { + cache.bottom = texture->higher; + + if (cache.bottom) + cache.bottom->lower = NULL; + } + else if (texture == cache.top) + { + cache.top = texture->lower; + + if (cache.top) + cache.top->higher = NULL; + } + else + { + texture->higher->lower = texture->lower; + texture->lower->higher = texture->higher; + } + +#ifdef __HASHMAP_OPT + CachedTexture* tex= cache.hash.find(texture->crc); + if (tex == texture); + cache.hash.insert(texture->crc, NULL); +#endif + + glDeleteTextures( 1, &texture->glName ); + cache.cachedBytes -= texture->textureBytes; + free( texture ); + + cache.numCached--; +} + +CachedTexture *TextureCache_AddTop() +{ + while (cache.cachedBytes > TEXTURECACHE_MAX) + { + if (cache.bottom != cache.dummy) + TextureCache_RemoveBottom(); + else if (cache.dummy->higher) + TextureCache_Remove( cache.dummy->higher ); + } + + CachedTexture *newtop = (CachedTexture*)malloc( sizeof( CachedTexture ) ); + + glGenTextures( 1, &newtop->glName ); + + newtop->lower = cache.top; + newtop->higher = NULL; + + if (cache.top) + cache.top->higher = newtop; + + if (!cache.bottom) + cache.bottom = newtop; + + cache.top = newtop; + + cache.numCached++; + + return newtop; +} + +void TextureCache_MoveToTop( CachedTexture *newtop ) +{ + if (newtop == cache.top) return; + + if (newtop == cache.bottom) + { + cache.bottom = newtop->higher; + cache.bottom->lower = NULL; + } + else + { + newtop->higher->lower = newtop->lower; + newtop->lower->higher = newtop->higher; + } + + newtop->higher = NULL; + newtop->lower = cache.top; + cache.top->higher = newtop; + cache.top = newtop; +} + +void TextureCache_Destroy() +{ + while (cache.bottom) + TextureCache_RemoveBottom(); + + glDeleteTextures( 32, cache.glNoiseNames ); + glDeleteTextures( 1, &cache.dummy->glName ); + +#ifdef __HASHMAP_OPT + cache.hash.destroy(); +#endif + + cache.top = NULL; + cache.bottom = NULL; +} + + + +void TextureCache_LoadBackground( CachedTexture *texInfo ) +{ + u32 *dest, *scaledDest; + u8 *swapped, *src; + u32 numBytes, bpl; + u32 x, y, j, tx, ty; + u16 clampSClamp, clampTClamp; + + int bytePerPixel=0; + TextureFormat texFormat; + GetTexelFunc getTexel; + GLint glWidth=0, glHeight=0; + GLenum glType=0; + GLenum glFormat=0; + + __texture_format(texInfo->size, texInfo->format, &texFormat); + +#ifdef PRINT_TEXTUREFORMAT + printf("BG LUT=%i, TEXTURE SIZE=%i, FORMAT=%i -> GL FORMAT=%i\n", gDP.otherMode.textureLUT, texInfo->size, texInfo->format, texFormat.format); fflush(stdout); +#endif + + if (texFormat.format == FORMAT_NONE) + { + LOG(LOG_WARNING, "No Texture Conversion function available, size=%i format=%i\n", texInfo->size, texInfo->format); + } + + switch(texFormat.format) + { + case FORMAT_I8: + glFormat = GL_LUMINANCE; + glType = GL_UNSIGNED_BYTE; + bytePerPixel = 1; + break; + case FORMAT_IA88: + glFormat = GL_LUMINANCE_ALPHA; + glType = GL_UNSIGNED_BYTE; + bytePerPixel = 2; + break; + case FORMAT_RGBA4444: + glFormat = GL_RGBA; + glType = GL_UNSIGNED_SHORT_4_4_4_4; + bytePerPixel = 2; + break; + case FORMAT_RGBA5551: + glFormat = GL_RGBA; + glType = GL_UNSIGNED_SHORT_5_5_5_1; + bytePerPixel = 2; + break; + case FORMAT_RGBA8888: + glFormat = GL_RGBA; + glType = GL_UNSIGNED_BYTE; + bytePerPixel = 4; + break; + } + + glWidth = texInfo->realWidth; + glHeight = texInfo->realHeight; + texInfo->textureBytes = (glWidth * glHeight) * bytePerPixel; + getTexel = texFormat.getTexel; + + bpl = gSP.bgImage.width << gSP.bgImage.size >> 1; + numBytes = bpl * gSP.bgImage.height; + swapped = (u8*) malloc(numBytes); + dest = (u32*) malloc(texInfo->textureBytes); + + if (!dest || !swapped) + { + LOG(LOG_ERROR, "Malloc failed!\n"); + return; + } + + UnswapCopy(&RDRAM[gSP.bgImage.address], swapped, numBytes); + + clampSClamp = texInfo->width - 1; + clampTClamp = texInfo->height - 1; + + j = 0; + for (y = 0; y < texInfo->realHeight; y++) + { + ty = min(y, clampTClamp); + src = &swapped[bpl * ty]; + for (x = 0; x < texInfo->realWidth; x++) + { + tx = min(x, clampSClamp); + if (bytePerPixel == 4) + ((u32*)dest)[j++] = getTexel(src, tx, 0, texInfo->palette); + else if (bytePerPixel == 2) + ((u16*)dest)[j++] = getTexel(src, tx, 0, texInfo->palette); + else if (bytePerPixel == 1) + ((u8*)dest)[j++] = getTexel(src, tx, 0, texInfo->palette); + } + } + + if (!config.texture.sai2x || (texFormat.format == FORMAT_I8 || texFormat.format == FORMAT_IA88)) + { + glTexImage2D( GL_TEXTURE_2D, 0, glFormat, glWidth, glHeight, 0, glFormat, glType, dest); + } + else + { + LOG(LOG_VERBOSE, "Using 2xSAI Filter on Texture\n"); + texInfo->textureBytes <<= 2; + + scaledDest = (u32*) malloc( texInfo->textureBytes ); + + if (glType == GL_UNSIGNED_BYTE) + _2xSaI8888( (u32*)dest, (u32*)scaledDest, texInfo->realWidth, texInfo->realHeight, texInfo->clampS, texInfo->clampT ); + if (glType == GL_UNSIGNED_SHORT_4_4_4_4) + _2xSaI4444( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, texInfo->clampS, texInfo->clampT ); + else + _2xSaI5551( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, texInfo->clampS, texInfo->clampT ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texInfo->realWidth << 1, texInfo->realHeight << 1, 0, GL_RGBA, glType, scaledDest ); + + free( scaledDest ); + } + + free(dest); + free(swapped); + + + if (config.texture.enableMipmap) + glGenerateMipmap(GL_TEXTURE_2D); +} + +void TextureCache_Load( CachedTexture *texInfo ) +{ + u32 *dest, *scaledDest; + + void *src; + u16 x, y, i, j, tx, ty, line; + u16 mirrorSBit, maskSMask, clampSClamp; + u16 mirrorTBit, maskTMask, clampTClamp; + + int bytePerPixel=0; + TextureFormat texFormat; + GetTexelFunc getTexel; + GLint glWidth=0, glHeight=0; + GLenum glType=0; + GLenum glFormat=0; + + __texture_format(texInfo->size, texInfo->format, &texFormat); + +#ifdef PRINT_TEXTUREFORMAT + printf("TEX LUT=%i, TEXTURE SIZE=%i, FORMAT=%i -> GL FORMAT=%i\n", gDP.otherMode.textureLUT, texInfo->size, texInfo->format, texFormat.format); fflush(stdout); +#endif + + if (texFormat.format == FORMAT_NONE) + { + LOG(LOG_WARNING, "No Texture Conversion function available, size=%i format=%i\n", texInfo->size, texInfo->format); + } + + switch(texFormat.format) + { + case FORMAT_I8: + glFormat = GL_LUMINANCE; + glType = GL_UNSIGNED_BYTE; + bytePerPixel = 1; + break; + case FORMAT_IA88: + glFormat = GL_LUMINANCE_ALPHA; + glType = GL_UNSIGNED_BYTE; + bytePerPixel = 2; + break; + case FORMAT_RGBA4444: + glFormat = GL_RGBA; + glType = GL_UNSIGNED_SHORT_4_4_4_4; + bytePerPixel = 2; + break; + case FORMAT_RGBA5551: + glFormat = GL_RGBA; + glType = GL_UNSIGNED_SHORT_5_5_5_1; + bytePerPixel = 2; + break; + case FORMAT_RGBA8888: + glFormat = GL_RGBA; + glType = GL_UNSIGNED_BYTE; + bytePerPixel = 4; + break; + } + + glWidth = texInfo->realWidth; + glHeight = texInfo->realHeight; + texInfo->textureBytes = (glWidth * glHeight) * bytePerPixel; + getTexel = texFormat.getTexel; + + dest = (u32*)malloc(texInfo->textureBytes); + + if (!dest) + { + LOG(LOG_ERROR, "Malloc failed!\n"); + return; + } + + + line = texInfo->line; + + if (texInfo->size == G_IM_SIZ_32b) + line <<= 1; + + if (texInfo->maskS) + { + clampSClamp = texInfo->clampS ? texInfo->clampWidth - 1 : (texInfo->mirrorS ? (texInfo->width << 1) - 1 : texInfo->width - 1); + maskSMask = (1 << texInfo->maskS) - 1; + mirrorSBit = texInfo->mirrorS ? (1 << texInfo->maskS) : 0; + } + else + { + clampSClamp = min( texInfo->clampWidth, texInfo->width ) - 1; + maskSMask = 0xFFFF; + mirrorSBit = 0x0000; + } + + if (texInfo->maskT) + { + clampTClamp = texInfo->clampT ? texInfo->clampHeight - 1 : (texInfo->mirrorT ? (texInfo->height << 1) - 1: texInfo->height - 1); + maskTMask = (1 << texInfo->maskT) - 1; + mirrorTBit = texInfo->mirrorT ? (1 << texInfo->maskT) : 0; + } + else + { + clampTClamp = min( texInfo->clampHeight, texInfo->height ) - 1; + maskTMask = 0xFFFF; + mirrorTBit = 0x0000; + } + + // Hack for Zelda warp texture + if (((texInfo->tMem << 3) + (texInfo->width * texInfo->height << texInfo->size >> 1)) > 4096) + { + texInfo->tMem = 0; + } + + // limit clamp values to min-0 (Perfect Dark has height=0 textures, making negative clamps) + if (clampTClamp & 0x8000) clampTClamp = 0; + if (clampSClamp & 0x8000) clampSClamp = 0; + + j = 0; + for (y = 0; y < texInfo->realHeight; y++) + { + ty = min(y, clampTClamp) & maskTMask; + if (y & mirrorTBit) ty ^= maskTMask; + src = &TMEM[(texInfo->tMem + line * ty) & 511]; + i = (ty & 1) << 1; + for (x = 0; x < texInfo->realWidth; x++) + { + tx = min(x, clampSClamp) & maskSMask; + + if (x & mirrorSBit) tx ^= maskSMask; + + if (bytePerPixel == 4) + { + ((u32*)dest)[j] = getTexel(src, tx, i, texInfo->palette); + } + else if (bytePerPixel == 2) + { + ((u16*)dest)[j] = getTexel(src, tx, i, texInfo->palette); + } + else if (bytePerPixel == 1) + { + ((u8*)dest)[j] = getTexel(src, tx, i, texInfo->palette); + } + j++; + } + } + + if (!config.texture.sai2x || (texFormat.format == FORMAT_I8) || (texFormat.format == FORMAT_IA88)) + { +#ifdef PRINT_TEXTUREFORMAT + printf("j=%u DEST=0x%x SIZE=%i F=0x%x, W=%i, H=%i, T=0x%x\n", j, dest, texInfo->textureBytes,glFormat, glWidth, glHeight, glType); fflush(stdout); +#endif + glTexImage2D( GL_TEXTURE_2D, 0, glFormat, glWidth, glHeight, 0, glFormat, glType, dest); + } + else + { + LOG(LOG_VERBOSE, "Using 2xSAI Filter on Texture\n"); + + texInfo->textureBytes <<= 2; + + scaledDest = (u32*)malloc( texInfo->textureBytes ); + + if (glType == GL_UNSIGNED_BYTE) + _2xSaI8888( (u32*)dest, (u32*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 ); + else if (glType == GL_UNSIGNED_SHORT_4_4_4_4) + _2xSaI4444( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 ); + else + _2xSaI5551( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texInfo->realWidth << 1, texInfo->realHeight << 1, 0, GL_RGBA, glType, scaledDest ); + + free( scaledDest ); + } + + free(dest); + + if (config.texture.enableMipmap) + glGenerateMipmap(GL_TEXTURE_2D); + +} + +#define max(a,b) ((a) > (b) ? (a) : (b)) + +u32 TextureCache_CalculateCRC( u32 t, u32 width, u32 height ) +{ + u32 crc; + u32 y, /*i,*/ bpl, lineBytes, line; + void *src; + + bpl = width << gSP.textureTile[t]->size >> 1; + lineBytes = gSP.textureTile[t]->line << 3; + + line = gSP.textureTile[t]->line; + if (gSP.textureTile[t]->size == G_IM_SIZ_32b) + line <<= 1; + + crc = 0xFFFFFFFF; + +#ifdef __CRC_OPT + unsigned n = (config.texture.fastCRC) ? max(1, height / 8) : 1; +#else + unsigned n = 1; +#endif + + for (y = 0; y < height; y += n) + { + src = (void*) &TMEM[(gSP.textureTile[t]->tmem + (y * line)) & 511]; + crc = CRC_Calculate( crc, src, bpl ); + } + + if (gSP.textureTile[t]->format == G_IM_FMT_CI) + { + if (gSP.textureTile[t]->size == G_IM_SIZ_4b) + crc = CRC_Calculate( crc, &gDP.paletteCRC16[gSP.textureTile[t]->palette], 4 ); + else if (gSP.textureTile[t]->size == G_IM_SIZ_8b) + crc = CRC_Calculate( crc, &gDP.paletteCRC256, 4 ); + } + return crc; +} + +void TextureCache_ActivateTexture( u32 t, CachedTexture *texture ) +{ + +#ifdef __HASHMAP_OPT + cache.hash.insert(texture->crc, texture); +#endif + + glActiveTexture( GL_TEXTURE0 + t ); + glBindTexture( GL_TEXTURE_2D, texture->glName ); + + // Set filter mode. Almost always bilinear, but check anyways + if ((gDP.otherMode.textureFilter == G_TF_BILERP) || (gDP.otherMode.textureFilter == G_TF_AVERAGE) || (config.texture.forceBilinear)) + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + else + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } + + // Set clamping modes + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (texture->clampS) ? GL_CLAMP_TO_EDGE : GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (texture->clampT) ? GL_CLAMP_TO_EDGE : GL_REPEAT ); + + if (config.texture.maxAnisotropy > 0) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, config.texture.maxAnisotropy); + } + + texture->lastDList = RSP.DList; + TextureCache_MoveToTop( texture ); + cache.current[t] = texture; +} + +void TextureCache_ActivateDummy( u32 t) +{ + glActiveTexture(GL_TEXTURE0 + t); + glBindTexture(GL_TEXTURE_2D, cache.dummy->glName ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +} + +int _background_compare(CachedTexture *current, u32 crc) +{ + if ((current != NULL) && + (current->crc == crc) && + (current->width == gSP.bgImage.width) && + (current->height == gSP.bgImage.height) && + (current->format == gSP.bgImage.format) && + (current->size == gSP.bgImage.size)) + return 1; + else + return 0; +} + +void TextureCache_UpdateBackground() +{ + u32 numBytes = gSP.bgImage.width * gSP.bgImage.height << gSP.bgImage.size >> 1; + u32 crc; + + crc = CRC_Calculate( 0xFFFFFFFF, &RDRAM[gSP.bgImage.address], numBytes ); + + if (gSP.bgImage.format == G_IM_FMT_CI) + { + if (gSP.bgImage.size == G_IM_SIZ_4b) + crc = CRC_Calculate( crc, &gDP.paletteCRC16[gSP.bgImage.palette], 4 ); + else if (gSP.bgImage.size == G_IM_SIZ_8b) + crc = CRC_Calculate( crc, &gDP.paletteCRC256, 4 ); + } + + //before we traverse cache, check to see if texture is already bound: + if (_background_compare(cache.current[0], crc)) + { + return; + } + +#ifdef __HASHMAP_OPT + CachedTexture *tex = cache.hash.find(crc); + if (tex) + { + if (_background_compare(tex, crc)) + { + TextureCache_ActivateTexture(0, tex); + cache.hits++; + return; + } + } +#endif + + CachedTexture *current = cache.top; + while (current) + { + if (_background_compare(current, crc)) + { + TextureCache_ActivateTexture( 0, current ); + cache.hits++; + return; + } + current = current->lower; + } + cache.misses++; + + glActiveTexture(GL_TEXTURE0); + cache.current[0] = TextureCache_AddTop(); + + glBindTexture( GL_TEXTURE_2D, cache.current[0]->glName ); + cache.current[0]->address = gSP.bgImage.address; + cache.current[0]->crc = crc; + cache.current[0]->format = gSP.bgImage.format; + cache.current[0]->size = gSP.bgImage.size; + cache.current[0]->width = gSP.bgImage.width; + cache.current[0]->height = gSP.bgImage.height; + cache.current[0]->clampWidth = gSP.bgImage.width; + cache.current[0]->clampHeight = gSP.bgImage.height; + cache.current[0]->palette = gSP.bgImage.palette; + cache.current[0]->maskS = 0; + cache.current[0]->maskT = 0; + cache.current[0]->mirrorS = 0; + cache.current[0]->mirrorT = 0; + cache.current[0]->clampS = 1; + cache.current[0]->clampT = 1; + cache.current[0]->line = 0; + cache.current[0]->tMem = 0; + cache.current[0]->lastDList = RSP.DList; + + cache.current[0]->realWidth = (config.texture.pow2) ? pow2(gSP.bgImage.width ) : gSP.bgImage.width; + cache.current[0]->realHeight = (config.texture.pow2) ? pow2(gSP.bgImage.height) : gSP.bgImage.height; + + cache.current[0]->scaleS = 1.0f / (f32)(cache.current[0]->realWidth); + cache.current[0]->scaleT = 1.0f / (f32)(cache.current[0]->realHeight); + cache.current[0]->shiftScaleS = 1.0f; + cache.current[0]->shiftScaleT = 1.0f; + + TextureCache_LoadBackground( cache.current[0] ); + TextureCache_ActivateTexture( 0, cache.current[0] ); + + cache.cachedBytes += cache.current[0]->textureBytes; +} + +int _texture_compare(u32 t, CachedTexture *current, u32 crc, u32 width, u32 height, u32 clampWidth, u32 clampHeight) +{ + if ((current != NULL) && + (current->crc == crc) && + (current->width == width) && + (current->height == height) && + (current->clampWidth == clampWidth) && + (current->clampHeight == clampHeight) && + (current->maskS == gSP.textureTile[t]->masks) && + (current->maskT == gSP.textureTile[t]->maskt) && + (current->mirrorS == gSP.textureTile[t]->mirrors) && + (current->mirrorT == gSP.textureTile[t]->mirrort) && + (current->clampS == gSP.textureTile[t]->clamps) && + (current->clampT == gSP.textureTile[t]->clampt) && + (current->format == gSP.textureTile[t]->format) && + (current->size == gSP.textureTile[t]->size)) + return 1; + else + return 0; +} + + +void TextureCache_Update( u32 t ) +{ + CachedTexture *current; + + u32 crc, maxTexels; + u32 tileWidth, maskWidth, loadWidth, lineWidth, clampWidth, height; + u32 tileHeight, maskHeight, loadHeight, lineHeight, clampHeight, width; + + if (gDP.textureMode == TEXTUREMODE_BGIMAGE) + { + TextureCache_UpdateBackground(); + return; + } + + TextureFormat texFormat; + __texture_format(gSP.textureTile[t]->size, gSP.textureTile[t]->format, &texFormat); + + maxTexels = texFormat.maxTexels; + + // Here comes a bunch of code that just calculates the texture size...I wish there was an easier way... + tileWidth = gSP.textureTile[t]->lrs - gSP.textureTile[t]->uls + 1; + tileHeight = gSP.textureTile[t]->lrt - gSP.textureTile[t]->ult + 1; + + maskWidth = 1 << gSP.textureTile[t]->masks; + maskHeight = 1 << gSP.textureTile[t]->maskt; + + loadWidth = gDP.loadTile->lrs - gDP.loadTile->uls + 1; + loadHeight = gDP.loadTile->lrt - gDP.loadTile->ult + 1; + + lineWidth = gSP.textureTile[t]->line << texFormat.lineShift; + + if (lineWidth) // Don't allow division by zero + lineHeight = min( maxTexels / lineWidth, tileHeight ); + else + lineHeight = 0; + + if (gDP.textureMode == TEXTUREMODE_TEXRECT) + { + u32 texRectWidth = gDP.texRect.width - gSP.textureTile[t]->uls; + u32 texRectHeight = gDP.texRect.height - gSP.textureTile[t]->ult; + + if (gSP.textureTile[t]->masks && ((maskWidth * maskHeight) <= maxTexels)) + width = maskWidth; + else if ((tileWidth * tileHeight) <= maxTexels) + width = tileWidth; + else if ((tileWidth * texRectHeight) <= maxTexels) + width = tileWidth; + else if ((texRectWidth * tileHeight) <= maxTexels) + width = gDP.texRect.width; + else if ((texRectWidth * texRectHeight) <= maxTexels) + width = gDP.texRect.width; + else if (gDP.loadType == LOADTYPE_TILE) + width = loadWidth; + else + width = lineWidth; + + if (gSP.textureTile[t]->maskt && ((maskWidth * maskHeight) <= maxTexels)) + height = maskHeight; + else if ((tileWidth * tileHeight) <= maxTexels) + height = tileHeight; + else if ((tileWidth * texRectHeight) <= maxTexels) + height = gDP.texRect.height; + else if ((texRectWidth * tileHeight) <= maxTexels) + height = tileHeight; + else if ((texRectWidth * texRectHeight) <= maxTexels) + height = gDP.texRect.height; + else if (gDP.loadType == LOADTYPE_TILE) + height = loadHeight; + else + height = lineHeight; + } + else + { + if (gSP.textureTile[t]->masks && ((maskWidth * maskHeight) <= maxTexels)) + width = maskWidth; + else if ((tileWidth * tileHeight) <= maxTexels) + width = tileWidth; + else if (gDP.loadType == LOADTYPE_TILE) + width = loadWidth; + else + width = lineWidth; + + if (gSP.textureTile[t]->maskt && ((maskWidth * maskHeight) <= maxTexels)) + height = maskHeight; + else if ((tileWidth * tileHeight) <= maxTexels) + height = tileHeight; + else if (gDP.loadType == LOADTYPE_TILE) + height = loadHeight; + else + height = lineHeight; + } + + clampWidth = gSP.textureTile[t]->clamps ? tileWidth : width; + clampHeight = gSP.textureTile[t]->clampt ? tileHeight : height; + + if (clampWidth > 256) + gSP.textureTile[t]->clamps = 0; + if (clampHeight > 256) + gSP.textureTile[t]->clampt = 0; + + // Make sure masking is valid + if (maskWidth > width) + { + gSP.textureTile[t]->masks = powof( width ); + maskWidth = 1 << gSP.textureTile[t]->masks; + } + + if (maskHeight > height) + { + gSP.textureTile[t]->maskt = powof( height ); + maskHeight = 1 << gSP.textureTile[t]->maskt; + } + + crc = TextureCache_CalculateCRC( t, width, height ); + + //before we traverse cache, check to see if texture is already bound: + if (_texture_compare(t, cache.current[t], crc, width, height, clampWidth, clampHeight)) + { + cache.hits++; + return; + } + +#ifdef __HASHMAP_OPT + CachedTexture *tex = cache.hash.find(crc); + if (tex) + { + if (_texture_compare(t, tex, crc, width, height, clampWidth, clampHeight)) + { + TextureCache_ActivateTexture( t, tex); + cache.hits++; + return; + } + } +#endif + + current = cache.top; + while (current) + { + if (_texture_compare(t, current, crc, width, height, clampWidth, clampHeight)) + { + TextureCache_ActivateTexture( t, current ); + cache.hits++; + return; + } + + current = current->lower; + } + + cache.misses++; + + glActiveTexture( GL_TEXTURE0 + t); + + cache.current[t] = TextureCache_AddTop(); + + if (cache.current[t] == NULL) + { + LOG(LOG_ERROR, "Texture Cache Failure\n"); + } + + glBindTexture( GL_TEXTURE_2D, cache.current[t]->glName ); + + cache.current[t]->address = gDP.textureImage.address; + cache.current[t]->crc = crc; + + cache.current[t]->format = gSP.textureTile[t]->format; + cache.current[t]->size = gSP.textureTile[t]->size; + + cache.current[t]->width = width; + cache.current[t]->height = height; + cache.current[t]->clampWidth = clampWidth; + cache.current[t]->clampHeight = clampHeight; + cache.current[t]->palette = gSP.textureTile[t]->palette; + cache.current[t]->maskS = gSP.textureTile[t]->masks; + cache.current[t]->maskT = gSP.textureTile[t]->maskt; + cache.current[t]->mirrorS = gSP.textureTile[t]->mirrors; + cache.current[t]->mirrorT = gSP.textureTile[t]->mirrort; + cache.current[t]->clampS = gSP.textureTile[t]->clamps; + cache.current[t]->clampT = gSP.textureTile[t]->clampt; + cache.current[t]->line = gSP.textureTile[t]->line; + cache.current[t]->tMem = gSP.textureTile[t]->tmem; + cache.current[t]->lastDList = RSP.DList; + + + if (cache.current[t]->clampS) + cache.current[t]->realWidth = (config.texture.pow2) ? pow2(clampWidth) : clampWidth; + else if (cache.current[t]->mirrorS) + cache.current[t]->realWidth = maskWidth << 1; + else + cache.current[t]->realWidth = (config.texture.pow2) ? pow2(width) : width; + + if (cache.current[t]->clampT) + cache.current[t]->realHeight = (config.texture.pow2) ? pow2(clampHeight) : clampHeight; + else if (cache.current[t]->mirrorT) + cache.current[t]->realHeight = maskHeight << 1; + else + cache.current[t]->realHeight = (config.texture.pow2) ? pow2(height) : height; + + + cache.current[t]->scaleS = 1.0f / (f32)(cache.current[t]->realWidth); + cache.current[t]->scaleT = 1.0f / (f32)(cache.current[t]->realHeight); + + // Hack for Zelda Sun + if ((config.hackZelda) && (gDP.combine.mux == 0x00262a60150c937fLL)) + { + if ((cache.current[t]->format = G_IM_FMT_I) && (cache.current[t]->size == G_IM_SIZ_8b) && + (cache.current[t]->width == 64)) + { + cache.current[t]->scaleS *= 0.5f; + cache.current[t]->scaleT *= 0.5f; + } + } + + cache.current[t]->shiftScaleS = 1.0f; + cache.current[t]->shiftScaleT = 1.0f; + + cache.current[t]->offsetS = config.texture.sai2x ? 0.25f : 0.5f; + cache.current[t]->offsetT = config.texture.sai2x ? 0.25f : 0.5f; + + if (gSP.textureTile[t]->shifts > 10) + cache.current[t]->shiftScaleS = (f32)(1 << (16 - gSP.textureTile[t]->shifts)); + else if (gSP.textureTile[t]->shifts > 0) + cache.current[t]->shiftScaleS /= (f32)(1 << gSP.textureTile[t]->shifts); + + if (gSP.textureTile[t]->shiftt > 10) + cache.current[t]->shiftScaleT = (f32)(1 << (16 - gSP.textureTile[t]->shiftt)); + else if (gSP.textureTile[t]->shiftt > 0) + cache.current[t]->shiftScaleT /= (f32)(1 << gSP.textureTile[t]->shiftt); + + TextureCache_Load( cache.current[t] ); + TextureCache_ActivateTexture( t, cache.current[t] ); + + cache.cachedBytes += cache.current[t]->textureBytes; +} + +void TextureCache_ActivateNoise(u32 t) +{ + glActiveTexture(GL_TEXTURE0 + t); + glBindTexture(GL_TEXTURE_2D, cache.glNoiseNames[RSP.DList & 0x1F]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); +} +