1
0
mirror of https://github.com/blawar/GLideN64.git synced 2024-07-07 03:13:49 +00:00
GLideN64/Textures.cpp
Sergey Lipskiy f7e0aa6e10 Refactor: correct texture CRC calculation.
Texture parameters are added to calculated crc, and thus the crc must
define the texture completely.
Assertion added to check that found texture is exactly the one we are looking for.
2015-05-13 10:12:41 +06:00

1144 lines
33 KiB
C++

#include <memory.h>
#include <algorithm>
#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"
#include "Config.h"
#include <assert.h>
using namespace std;
TextureCache cache;
typedef u32 (*GetTexelFunc)( u64 *src, u16 x, u16 i, u8 palette );
inline u32 GetNone( u64 *src, u16 x, u16 i, u8 palette )
{
return 0x00000000;
}
inline u32 GetCI4IA_RGBA4444( u64 *src, u16 x, u16 i, u8 palette )
{
u8 color4B;
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)] );
}
inline u32 GetCI4IA_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
u8 color4B;
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)] );
}
inline u32 GetCI4RGBA_RGBA5551( u64 *src, u16 x, u16 i, u8 palette )
{
u8 color4B;
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)] );
}
inline u32 GetCI4RGBA_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
u8 color4B;
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)] );
}
inline u32 GetIA31_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
u8 color4B;
color4B = ((u8*)src)[(x>>1)^(i<<1)];
return IA31_RGBA8888( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
}
inline u32 GetIA31_RGBA4444( u64 *src, u16 x, u16 i, u8 palette )
{
u8 color4B;
color4B = ((u8*)src)[(x>>1)^(i<<1)];
return IA31_RGBA4444( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
}
inline u32 GetI4_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
u8 color4B;
color4B = ((u8*)src)[(x>>1)^(i<<1)];
return I4_RGBA8888( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
}
inline u32 GetI4_RGBA4444( u64 *src, u16 x, u16 i, u8 palette )
{
u8 color4B;
color4B = ((u8*)src)[(x>>1)^(i<<1)];
return I4_RGBA4444( (x & 1) ? (color4B & 0x0F) : (color4B >> 4) );
}
inline u32 GetCI8IA_RGBA4444( u64 *src, u16 x, u16 i, u8 palette )
{
return IA88_RGBA4444( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] );
}
inline u32 GetCI8IA_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
return IA88_RGBA8888( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] );
}
inline u32 GetCI8RGBA_RGBA5551( u64 *src, u16 x, u16 i, u8 palette )
{
return RGBA5551_RGBA5551( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] );
}
inline u32 GetCI8RGBA_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
return RGBA5551_RGBA8888( *(u16*)&TMEM[256 + ((u8*)src)[x^(i<<1)]] );
}
inline u32 GetIA44_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
return IA44_RGBA8888(((u8*)src)[x^(i<<1)]);
}
inline u32 GetIA44_RGBA4444( u64 *src, u16 x, u16 i, u8 palette )
{
return IA44_RGBA4444(((u8*)src)[x^(i<<1)]);
}
inline u32 GetI8_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
return I8_RGBA8888(((u8*)src)[x^(i<<1)]);
}
inline u32 GetI8_RGBA4444( u64 *src, u16 x, u16 i, u8 palette )
{
return I8_RGBA4444(((u8*)src)[x^(i<<1)]);
}
inline u32 GetRGBA5551_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
return RGBA5551_RGBA8888( ((u16*)src)[x^i] );
}
inline u32 GetRGBA5551_RGBA5551( u64 *src, u16 x, u16 i, u8 palette )
{
return RGBA5551_RGBA5551( ((u16*)src)[x^i] );
}
inline u32 GetIA88_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
return IA88_RGBA8888(((u16*)src)[x^i]);
}
inline u32 GetIA88_RGBA4444( u64 *src, u16 x, u16 i, u8 palette )
{
return IA88_RGBA4444(((u16*)src)[x^i]);
}
inline u32 GetRGBA8888_RGBA8888( u64 *src, u16 x, u16 i, u8 palette )
{
return ((u32*)src)[x^i];
}
inline u32 GetRGBA8888_RGBA4444( u64 *src, u16 x, u16 i, u8 palette )
{
return RGBA8888_RGBA4444(((u32*)src)[x^i]);
}
const struct
{
GetTexelFunc Get16;
GLenum glType16;
GLint glInternalFormat16;
GetTexelFunc Get32;
GLenum glType32;
GLint glInternalFormat32;
u32 autoFormat, lineShift, maxTexels;
} imageFormat[4][5] =
{ // Get16 glType16 glInternalFormat16 Get32 glType32 glInternalFormat32 autoFormat
{ // 4-bit
{ GetCI4RGBA_RGBA5551, GL_UNSIGNED_SHORT_5_5_5_1, GL_RGB5_A1, GetCI4RGBA_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGB5_A1, 4, 4096 }, // CI (Banjo-Kazooie uses this, doesn't make sense, but it works...)
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 4, 8192 }, // YUV
{ GetCI4RGBA_RGBA5551, GL_UNSIGNED_SHORT_5_5_5_1, GL_RGB5_A1, GetCI4RGBA_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGB5_A1, 4, 4096 }, // CI
{ GetIA31_RGBA4444, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetIA31_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 4, 8192 }, // IA
{ GetI4_RGBA4444, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetI4_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 4, 8192 }, // I
},
{ // 8-bit
{ GetCI8RGBA_RGBA5551, GL_UNSIGNED_SHORT_5_5_5_1, GL_RGB5_A1, GetCI8RGBA_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGB5_A1, 3, 2048 }, // RGBA
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 0, 4096 }, // YUV
{ GetCI8RGBA_RGBA5551, GL_UNSIGNED_SHORT_5_5_5_1, GL_RGB5_A1, GetCI8RGBA_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGB5_A1, 3, 2048 }, // CI
{ GetIA44_RGBA4444, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetIA44_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 3, 4096 }, // IA
{ GetI8_RGBA4444, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetI8_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA, 3, 4096 }, // I
},
{ // 16-bit
{ GetRGBA5551_RGBA5551, GL_UNSIGNED_SHORT_5_5_5_1, GL_RGB5_A1, GetRGBA5551_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGB5_A1, 2, 2048 }, // RGBA
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 2, 2048 }, // YUV
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 0, 2048 }, // CI
{ GetIA88_RGBA4444, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetIA88_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA, 2, 2048 }, // IA
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 0, 2048 }, // I
},
{ // 32-bit
{ GetRGBA8888_RGBA4444, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetRGBA8888_RGBA8888, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA, 2, 1024 }, // RGBA
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 0, 1024 }, // YUV
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 0, 1024 }, // CI
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 0, 1024 }, // IA
{ GetNone, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA4, GetNone, GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA4, 0, 1024 }, // I
}
};
void TextureCache_Init()
{
u32 dummyTexture[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
cache.current[0] = NULL;
cache.current[1] = NULL;
cache.top = NULL;
cache.bottom = NULL;
cache.numCached = 0;
cache.cachedBytes = 0;
cache.enable2xSaI = config.texture.enable2xSaI;
cache.bitDepth = config.texture.textureBitDepth;
glGenTextures( 32, cache.glNoiseNames );
cache.dummy = TextureCache_AddTop();
cache.dummy->address = 0;
cache.dummy->clampS = 1;
cache.dummy->clampT = 1;
cache.dummy->clampWidth = 2;
cache.dummy->clampHeight = 2;
cache.dummy->crc = 0;
cache.dummy->format = 0;
cache.dummy->size = 0;
cache.dummy->frameBufferTexture = FALSE;
cache.dummy->width = 2;
cache.dummy->height = 2;
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, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummyTexture );
cache.cachedBytes = cache.dummy->textureBytes;
TextureCache_ActivateDummy( 0 );
TextureCache_ActivateDummy( 1 );
CRC_BuildTable();
}
#ifdef TEXTURE_CACHE_VERIFICATION
static
bool TextureCache_Verify()
{
s16 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;
}
#else
static
bool TextureCache_Verify() {return true;}
#endif
void TextureCache_RemoveBottom()
{
CachedTexture *newBottom = cache.bottom->higher;
glDeleteTextures( 1, &cache.bottom->glName );
cache.cachedBytes -= cache.bottom->textureBytes;
if (cache.bottom->frameBufferTexture)
FrameBuffer_RemoveBuffer( cache.bottom->address );
if (cache.bottom == cache.top)
cache.top = NULL;
free( cache.bottom );
cache.bottom = newBottom;
if (cache.bottom)
cache.bottom->lower = NULL;
cache.numCached--;
assert(TextureCache_Verify());
}
void TextureCache_Remove( CachedTexture *texture )
{
if (texture == NULL)
return;
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;
}
glDeleteTextures( 1, &texture->glName );
cache.cachedBytes -= texture->textureBytes;
free( texture );
cache.numCached--;
assert(TextureCache_Verify());
}
CachedTexture *TextureCache_AddTop()
{
while (cache.cachedBytes > cache.maxBytes)
{
if (cache.bottom != cache.dummy)
TextureCache_RemoveBottom();
else if (cache.dummy->higher) {
CachedTexture *pCurrent = cache.dummy->higher;
while (pCurrent != NULL && pCurrent->frameBufferTexture != 0)
pCurrent = pCurrent->higher;
TextureCache_Remove(pCurrent);
}
}
CachedTexture *newtop = (CachedTexture*)malloc( sizeof( CachedTexture ) );
memset(newtop, 0, 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++;
assert(TextureCache_Verify());
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;
assert(TextureCache_Verify());
}
void TextureCache_Destroy()
{
while (cache.bottom)
TextureCache_RemoveBottom();
glDeleteTextures( 32, cache.glNoiseNames );
// glDeleteTextures( 1, &cache.glDummyName );
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;
u16 clampTClamp;
GetTexelFunc GetTexel;
GLuint glInternalFormat;
GLenum glType;
if (((imageFormat[texInfo->size][texInfo->format].autoFormat == GL_RGBA) ||
((texInfo->format == G_IM_FMT_CI) && (gDP.otherMode.textureLUT == G_TT_IA16)) || (cache.bitDepth == 2)) && (cache.bitDepth != 0))
{
texInfo->textureBytes = (texInfo->realWidth * texInfo->realHeight) << 2;
if ((texInfo->format == G_IM_FMT_CI) && (gDP.otherMode.textureLUT == G_TT_IA16))
{
if (texInfo->size == G_IM_SIZ_4b)
GetTexel = GetCI4IA_RGBA8888;
else
GetTexel = GetCI8IA_RGBA8888;
glInternalFormat = GL_RGBA;
glType = GL_UNSIGNED_BYTE;
}
else
{
GetTexel = imageFormat[texInfo->size][texInfo->format].Get32;
glInternalFormat = imageFormat[texInfo->size][texInfo->format].glInternalFormat32;
glType = imageFormat[texInfo->size][texInfo->format].glType32;
}
}
else
{
texInfo->textureBytes = (texInfo->realWidth * texInfo->realHeight) << 1;
if ((texInfo->format == G_IM_FMT_CI) && (gDP.otherMode.textureLUT == G_TT_IA16))
{
if (texInfo->size == G_IM_SIZ_4b)
GetTexel = GetCI4IA_RGBA4444;
else
GetTexel = GetCI8IA_RGBA4444;
glInternalFormat = GL_RGBA4;
glType = GL_UNSIGNED_SHORT_4_4_4_4;
}
else
{
GetTexel = imageFormat[texInfo->size][texInfo->format].Get16;
glInternalFormat = imageFormat[texInfo->size][texInfo->format].glInternalFormat16;
glType = imageFormat[texInfo->size][texInfo->format].glType16;
}
}
bpl = gSP.bgImage.width << gSP.bgImage.size >> 1;
numBytes = bpl * gSP.bgImage.height;
swapped = (u8*)malloc( numBytes );
UnswapCopy( &RDRAM[gSP.bgImage.address], swapped, numBytes );
dest = (u32*)malloc( texInfo->textureBytes );
clampSClamp = texInfo->width - 1;
clampTClamp = texInfo->height - 1;
j = 0;
for (y = 0; y < texInfo->realHeight; y++)
{
ty = min(y, (u32)clampTClamp);
src = &swapped[bpl * ty];
for (x = 0; x < texInfo->realWidth; x++)
{
tx = min(x, (u32)clampSClamp);
if (glInternalFormat == GL_RGBA)
((u32*)dest)[j++] = GetTexel( (u64*)src, tx, 0, texInfo->palette );
else
((u16*)dest)[j++] = GetTexel( (u64*)src, tx, 0, texInfo->palette );
}
}
if (cache.enable2xSaI)
{
texInfo->textureBytes <<= 2;
scaledDest = (u32*)malloc( texInfo->textureBytes );
if (glInternalFormat == GL_RGBA)
_2xSaI8888( (u32*)dest, (u32*)scaledDest, texInfo->realWidth, texInfo->realHeight, texInfo->clampS, texInfo->clampT );
else if (glInternalFormat == GL_RGBA4)
_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, glInternalFormat, texInfo->realWidth << 1, texInfo->realHeight << 1, 0, GL_RGBA, glType, scaledDest );
free( dest );
free( scaledDest );
}
else
{
glTexImage2D( GL_TEXTURE_2D, 0, glInternalFormat, texInfo->realWidth, texInfo->realHeight, 0, GL_RGBA, glType, dest );
free( dest );
}
}
void TextureCache_Load( CachedTexture *texInfo )
{
u32 *dest, *scaledDest;
u64 *src;
u16 x, y, i, j, tx, ty, line;
u16 mirrorSBit, maskSMask, clampSClamp;
u16 mirrorTBit, maskTMask, clampTClamp;
GetTexelFunc GetTexel;
GLuint glInternalFormat;
GLenum glType;
if (((imageFormat[texInfo->size][texInfo->format].autoFormat == GL_RGBA) ||
((texInfo->format == G_IM_FMT_CI) && (gDP.otherMode.textureLUT == G_TT_IA16)) || (cache.bitDepth == 2)) && (cache.bitDepth != 0))
{
texInfo->textureBytes = (texInfo->realWidth * texInfo->realHeight) << 2;
if ((texInfo->format == G_IM_FMT_CI) && (gDP.otherMode.textureLUT == G_TT_IA16))
{
if (texInfo->size == G_IM_SIZ_4b)
GetTexel = GetCI4IA_RGBA8888;
else
GetTexel = GetCI8IA_RGBA8888;
glInternalFormat = GL_RGBA;
glType = GL_UNSIGNED_BYTE;
}
else
{
GetTexel = imageFormat[texInfo->size][texInfo->format].Get32;
glInternalFormat = imageFormat[texInfo->size][texInfo->format].glInternalFormat32;
glType = imageFormat[texInfo->size][texInfo->format].glType32;
}
}
else
{
texInfo->textureBytes = (texInfo->realWidth * texInfo->realHeight) << 1;
if ((texInfo->format == G_IM_FMT_CI) && (gDP.otherMode.textureLUT == G_TT_IA16))
{
if (texInfo->size == G_IM_SIZ_4b)
GetTexel = GetCI4IA_RGBA4444;
else
GetTexel = GetCI8IA_RGBA4444;
glInternalFormat = GL_RGBA4;
glType = GL_UNSIGNED_SHORT_4_4_4_4;
}
else
{
GetTexel = imageFormat[texInfo->size][texInfo->format].Get16;
glInternalFormat = imageFormat[texInfo->size][texInfo->format].glInternalFormat16;
glType = imageFormat[texInfo->size][texInfo->format].glType16;
}
}
dest = (u32*)malloc( texInfo->textureBytes );
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;
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;
i = (ty & 1) << 1;
for (x = 0; x < texInfo->realWidth; x++)
{
tx = min(x, clampSClamp) & maskSMask;
if (x & mirrorSBit)
tx ^= maskSMask;
if (glInternalFormat == GL_RGBA)
((u32*)dest)[j++] = GetTexel( src, tx, i, texInfo->palette );
else
((u16*)dest)[j++] = GetTexel( src, tx, i, texInfo->palette );
}
}
if (cache.enable2xSaI)
{
texInfo->textureBytes <<= 2;
scaledDest = (u32*)malloc( texInfo->textureBytes );
if (glInternalFormat == GL_RGBA)
_2xSaI8888( (u32*)dest, (u32*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 );//texInfo->clampS, texInfo->clampT );
else if (glInternalFormat == GL_RGBA4)
_2xSaI4444( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 );//texInfo->clampS, texInfo->clampT );
else
_2xSaI5551( (u16*)dest, (u16*)scaledDest, texInfo->realWidth, texInfo->realHeight, 1, 1 );//texInfo->clampS, texInfo->clampT );
glTexImage2D( GL_TEXTURE_2D, 0, glInternalFormat, texInfo->realWidth << 1, texInfo->realHeight << 1, 0, GL_RGBA, glType, scaledDest );
free( dest );
free( scaledDest );
}
else
{
glTexImage2D( GL_TEXTURE_2D, 0, glInternalFormat, texInfo->realWidth, texInfo->realHeight, 0, GL_RGBA, glType, dest );
free( dest );
}
}
struct TextureParams
{
u16 width;
u16 height;
u16 clampWidth;
u16 clampHeight;
u8 maskS;
u8 maskT;
u8 mirrorS;
u8 mirrorT;
u8 clampS;
u8 clampT;
u8 format;
u8 size;
};
u32 TextureCache_CalculateCRC(u32 t, const TextureParams & _params)
{
u32 crc;
u32 y, bpl, lineBytes, line;
u64 *src;
src = (u64*)&TMEM[gSP.textureTile[t]->tmem];
bpl = _params.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;
for (y = 0; y < _params.height; y++)
{
crc = CRC_Calculate( crc, src, bpl );
src += line;
}
if (gDP.otherMode.textureLUT != G_TT_NONE || 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 );
}
crc = CRC_Calculate(crc, &_params, sizeof(_params));
return crc;
}
void TextureCache_ActivateTexture( u32 t, CachedTexture *texture )
{
glActiveTexture( GL_TEXTURE0 + t );
// Bind the cached texture
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 );
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 );
}
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 (gDP.otherMode.textureLUT != G_TT_NONE || 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 );
}
u32 params[4] = {gSP.bgImage.width, gSP.bgImage.height, gSP.bgImage.format, gSP.bgImage.size};
crc = CRC_Calculate( crc, params, sizeof(u32)*4 );
CachedTexture *current = cache.top;
while (current)
{
if (current->crc == crc) {
assert((current->width == gSP.bgImage.width) &&
(current->height == gSP.bgImage.height) &&
(current->format == gSP.bgImage.format) &&
(current->size == gSP.bgImage.size));
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]->frameBufferTexture = FALSE;
cache.current[0]->realWidth = pow2( gSP.bgImage.width );
cache.current[0]->realHeight = pow2( 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;
}
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 (cache.enable2xSaI != config.texture.enable2xSaI)
{
TextureCache_Destroy();
TextureCache_Init();
}
if (cache.bitDepth != config.texture.textureBitDepth)
{
TextureCache_Destroy();
TextureCache_Init();
}
switch(gSP.textureTile[t]->textureMode) {
case TEXTUREMODE_BGIMAGE:
TextureCache_UpdateBackground();
return;
case TEXTUREMODE_FRAMEBUFFER:
FrameBuffer_ActivateBufferTexture( t, gSP.textureTile[t]->frameBuffer );
return;
case TEXTUREMODE_FRAMEBUFFER_BG:
FrameBuffer_ActivateBufferTextureBG( t, gSP.textureTile[t]->frameBuffer );
return;
}
maxTexels = imageFormat[gSP.textureTile[t]->size][gSP.textureTile[t]->format].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 << imageFormat[gSP.textureTile[t]->size][gSP.textureTile[t]->format].lineShift;
if (lineWidth) // Don't allow division by zero
lineHeight = min( maxTexels / lineWidth, tileHeight );
else
lineHeight = 0;
if (gSP.textureTile[t]->textureMode == TEXTUREMODE_TEXRECT)
{
u16 texRectWidth = gDP.texRect.width - gSP.textureTile[t]->uls;
u16 texRectHeight = gDP.texRect.height - gSP.textureTile[t]->ult;
// if ((tileWidth == (maskWidth + 1)) && (gDP.loadType == LOADTYPE_TILE) && (gDP.loadTile->lrs - gDP.loadTile->uls + 1 == tileWidth))
// gSP.textureTile[t]->masks = 0;
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 (gSP.textureTile[t]->loadType == LOADTYPE_TILE)
width = loadWidth;
else
width = lineWidth;
// if ((tileHeight == (maskHeight + 1)) && (gDP.loadType == LOADTYPE_TILE) && (gDP.loadTile->lrt - gDP.loadTile->ult + 1 == tileHeight))
// gSP.textureTile[t]->maskt = 0;
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 (gSP.textureTile[t]->loadType == LOADTYPE_TILE)
height = loadHeight;
else
height = lineHeight;
// gSP.textureTile[t]->masks = 0;
// gSP.textureTile[t]->maskt = 0;
}
else
{
if (gSP.textureTile[t]->masks && ((maskWidth * maskHeight) <= maxTexels))
width = maskWidth; // Use mask width if set and valid
else if ((tileWidth * tileHeight) <= maxTexels)
width = tileWidth; // else use tile width if valid
else if (gSP.textureTile[t]->loadType == LOADTYPE_TILE)
width = loadWidth; // else use load width if load done with LoadTile
else
width = lineWidth; // else use line-based width
if (gSP.textureTile[t]->maskt && ((maskWidth * maskHeight) <= maxTexels))
height = maskHeight;
else if ((tileWidth * tileHeight) <= maxTexels)
height = tileHeight;
else if (gSP.textureTile[t]->loadType == LOADTYPE_TILE)
height = loadHeight;
else
height = lineHeight;
}
/* if (gDP.loadTile->frameBuffer)
{
FrameBuffer_ActivateBufferTexture( t, gDP.loadTile->frameBuffer );
return;
}*/
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;
}
{
TextureParams params;
params.width = width;
params.height = height;
params.clampWidth = clampWidth;
params.clampHeight = clampHeight;
params.maskS = gSP.textureTile[t]->masks;
params.maskT = gSP.textureTile[t]->maskt;
params.mirrorS = gSP.textureTile[t]->mirrors;
params.mirrorT = gSP.textureTile[t]->mirrort;
params.clampS = gSP.textureTile[t]->clamps;
params.clampT = gSP.textureTile[t]->clampt;
params.format = gSP.textureTile[t]->format;
params.size = gSP.textureTile[t]->size;
crc = TextureCache_CalculateCRC( t, params );
}
// if (!TextureCache_Verify())
// current = cache.top;
current = cache.top;
while (current)
{
if (current->crc == crc) {
assert((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)
);
TextureCache_ActivateTexture( t, current );
cache.hits++;
return;
}
current = current->lower;
}
cache.misses++;
glActiveTexture( GL_TEXTURE0 + t );
cache.current[t] = TextureCache_AddTop();
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]->fulS = gSP.textureTile[t]->fulS;
cache.current[t]->fulT = gSP.textureTile[t]->fulT;
cache.current[t]->ulS = gSP.textureTile[t]->ulS;
cache.current[t]->ulT = gSP.textureTile[t]->ulT;
cache.current[t]->lrS = gSP.textureTile[t]->lrS;
cache.current[t]->lrT = gSP.textureTile[t]->lrT;*/
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;
cache.current[t]->frameBufferTexture = FALSE;
/* if (cache.current[t]->clampS)
cache.current[t]->realWidth = pow2( clampWidth );
else if (cache.current[t]->mirrorS)
cache.current[t]->realWidth = maskWidth << 1;
else
cache.current[t]->realWidth = pow2( width );
if (cache.current[t]->clampT)
cache.current[t]->realHeight = pow2( clampHeight );
else if (cache.current[t]->mirrorT)
cache.current[t]->realHeight = maskHeight << 1;
else
cache.current[t]->realHeight = pow2( height );*/
if (cache.current[t]->clampS)
cache.current[t]->realWidth = pow2( clampWidth );
else if (cache.current[t]->mirrorS)
cache.current[t]->realWidth = maskWidth << 1;
else
cache.current[t]->realWidth = pow2( width );
if (cache.current[t]->clampT)
cache.current[t]->realHeight = pow2( clampHeight );
else if (cache.current[t]->mirrorT)
cache.current[t]->realHeight = maskHeight << 1;
else
cache.current[t]->realHeight = pow2( height );
cache.current[t]->scaleS = 1.0f / (f32)(cache.current[t]->realWidth);
cache.current[t]->scaleT = 1.0f / (f32)(cache.current[t]->realHeight);
cache.current[t]->shiftScaleS = 1.0f;
cache.current[t]->shiftScaleT = 1.0f;
cache.current[t]->offsetS = config.texture.enable2xSaI ? 0.25f : 0.5f;
cache.current[t]->offsetT = config.texture.enable2xSaI ? 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 );
}