1
0
mirror of https://github.com/blawar/GLideN64.git synced 2024-07-02 09:03:37 +00:00
GLideN64/Textures.cpp

1115 lines
32 KiB
C++
Raw Normal View History

#include <memory.h>
2014-09-01 16:19:20 +00:00
#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"
2013-10-11 08:25:12 +00:00
#include <assert.h>
2014-09-01 16:19:20 +00:00
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
2014-04-17 06:24:53 +00:00
{ 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
2014-04-17 06:24:53 +00:00
{ 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
2014-04-17 06:24:53 +00:00
{ 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
2014-04-17 06:24:53 +00:00
{ 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 );
2014-04-17 06:24:53 +00:00
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();
}
2013-10-11 08:25:12 +00:00
#ifdef TEXTURE_CACHE_VERIFICATION
static
bool TextureCache_Verify()
{
s16 i = 0;
CachedTexture *current;
current = cache.top;
while (current)
{
i++;
current = current->lower;
}
2013-10-11 08:25:12 +00:00
if (i != cache.numCached) return false;
i = 0;
current = cache.bottom;
while (current)
{
i++;
current = current->higher;
}
2013-10-11 08:25:12 +00:00
if (i != cache.numCached) return false;
2013-10-11 08:25:12 +00:00
return true;
}
2013-10-11 08:25:12 +00:00
#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--;
2013-10-11 08:25:12 +00:00
assert(TextureCache_Verify());
}
void TextureCache_Remove( CachedTexture *texture )
{
2013-11-18 03:30:20 +00:00
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--;
2013-10-11 08:25:12 +00:00
assert(TextureCache_Verify());
}
CachedTexture *TextureCache_AddTop()
{
while (cache.cachedBytes > cache.maxBytes)
{
if (cache.bottom != cache.dummy)
TextureCache_RemoveBottom();
2013-10-11 08:26:45 +00:00
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 ) );
2013-10-11 08:25:55 +00:00
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++;
2013-10-11 08:25:12 +00:00
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;
2013-10-11 08:25:12 +00:00
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;
2014-04-17 06:24:53 +00:00
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;
2014-04-17 06:24:53 +00:00
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;
2014-04-17 06:24:53 +00:00
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++)
{
2014-09-01 16:19:20 +00:00
ty = min(y, (u32)clampTClamp);
src = &swapped[bpl * ty];
for (x = 0; x < texInfo->realWidth; x++)
{
2014-09-01 16:19:20 +00:00
tx = min(x, (u32)clampSClamp);
2014-04-17 06:24:53 +00:00
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 );
2014-04-17 06:24:53 +00:00
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;
2014-04-17 06:24:53 +00:00
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;
2014-04-17 06:24:53 +00:00
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;
2014-04-17 06:24:53 +00:00
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;
2014-04-17 06:24:53 +00:00
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 );
2014-04-17 06:24:53 +00:00
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 );
}
}
u32 TextureCache_CalculateCRC( u32 t, u32 width, u32 height )
{
u32 crc;
u32 y, bpl, lineBytes, line;
u64 *src;
src = (u64*)&TMEM[gSP.textureTile[t]->tmem];
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;
for (y = 0; y < height; y++)
{
crc = CRC_Calculate( crc, src, bpl );
src += line;
}
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 )
{
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 (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 );
}
CachedTexture *current = cache.top;
while (current)
{
if ((current->crc == crc) &&
(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;
}
crc = TextureCache_CalculateCRC( t, width, height );
// if (!TextureCache_Verify())
// current = cache.top;
current = cache.top;
while (current)
{
if ((current->crc == crc) &&
// (current->address == gDP.textureImage.address) &&
// (current->palette == gSP.textureTile[t]->palette) &&
(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->tMem == gSP.textureTile[t]->tMem) &&
/* (current->ulS == gSP.textureTile[t]->ulS) &&
(current->ulT == gSP.textureTile[t]->ulT) &&
(current->lrS == gSP.textureTile[t]->lrS) &&
(current->lrT == gSP.textureTile[t]->lrT) &&*/
(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 );
}