ppsspp/GPU/GLES/TextureCache.cpp

497 lines
11 KiB
C++
Raw Normal View History

2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 22:58:25 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#ifdef ANDROID
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#else
#include <GL/glew.h>
#if defined(__APPLE__)
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#endif
#include <map>
#include "../../Core/MemMap.h"
#include "../ge_constants.h"
#include "../GPUState.h"
#include "TextureCache.h"
struct TexCacheEntry
{
u32 addr;
u32 hash;
u32 frameCounter;
u32 numMips;
int dim;
GLuint texture;
};
typedef std::map<u32, TexCacheEntry> TexCache;
static TexCache cache;
u8 *tempArea;
void TextureCache_Clear(bool delete_them)
{
if (delete_them)
{
for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ++iter)
{
DEBUG_LOG(G3D, "Deleting texture %i", iter->second.texture);
glDeleteTextures(1, &iter->second.texture);
}
}
if (cache.size()) {
INFO_LOG(G3D, "Texture cached cleared from %i textures", (int)cache.size());
cache.clear();
}
}
u32 PaletteLoad(int index)
{
int pf = gstate.clutformat & 3;
int shift = (gstate.clutformat >> 2) & 31;
int mask = (gstate.clutformat >> 8) & 255;
int start = ((gstate.clutformat >> 16) & 31) * 16;
if (pf<3)
{
//16-bit
u16 *p = (u16*)gstate.paletteMem;
u16 col = p[((start+index)>>shift) & mask];
int r,g,b,a;
// TODO: properly expand the lower bits.
switch (pf)
{
case 0:
r = (col&0x1f)*8;
g = ((col>>5)&0x3f)*4;
b = ((col>>11)&0x1f)*8;
a = 255;
break;
case 1:
r = (col&0x1f)*8;
g = ((col>>5)&0x1f)*8;
b = ((col>>10)&0x1f)*8;
a = (col>>15)*255;
break;
case 2:
r = (col&0xf)*16;
g = ((col>>4)&0xf)*16;
b = ((col>>8)&0xf)*16;
a = ((col>>12)&0xF)*16;
break;
}
// We now use OpenGL ES 2.0 style colors.
return (a << 24) | (b<<16) | (g<<8) | (r<<0);
}
else
{
u32 *p = (u32*)gstate.paletteMem;
u32 col = p[((start + index) >> shift) & mask];
return col;
}
}
// This should not have to be done per texture! OpenGL is silly yo
// TODO: Dirty-check this against the current texture.
void UpdateSamplingParams()
{
int minFilt = gstate.texfilter & 0x7;
int magFilt = (gstate.texfilter>>8)&1;
minFilt &= 1; //no mipmaps yet
int sClamp = gstate.texwrap & 1;
int tClamp = (gstate.texwrap>>8) & 1;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilt ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilt ? GL_LINEAR : GL_NEAREST);
}
// Convert from PSP bit order to GLES bit order
u16 convert565(u16 c) {
return (c >> 11) | (c & 0x07E0) | (c << 11);
}
// Convert from PSP bit order to GLES bit order
u16 convert4444(u16 c) {
return (c >> 12) | ((c >> 4) & 0xF0) | ((c << 4) & 0xF00) | (c << 12);
}
// Convert from PSP bit order to GLES bit order
u16 convert5551(u16 c) {
return ((c & 0x8000) >> 15) | (c << 1);
}
struct DXT1Block
{
u8 lines[4];
u16 color1;
u16 color2;
};
inline u8 Convert5To8(u8 v)
{
// Swizzle bits: 00012345 -> 12345123
return (v << 3) | (v >> 2);
}
inline u8 Convert6To8(u8 v)
{
// Swizzle bits: 00123456 -> 12345612
return (v << 2) | (v >> 4);
}
inline u32 makecol(int r, int g, int b, int a)
{
return (a << 24)|(r << 16)|(g << 8)|b;
}
void decodeDXT1Block(u32 *dst, const DXT1Block *src, int pitch)
{
// S3TC Decoder
// Needs more speed and debugging.
u16 c1 = src->color1;
u16 c2 = src->color2;
int blue1 = Convert5To8(c1 & 0x1F);
int blue2 = Convert5To8(c2 & 0x1F);
int green1 = Convert6To8((c1 >> 5) & 0x3F);
int green2 = Convert6To8((c2 >> 5) & 0x3F);
int red1 = Convert5To8((c1 >> 11) & 0x1F);
int red2 = Convert5To8((c2 >> 11) & 0x1F);
int colors[4];
colors[0] = makecol(red1, green1, blue1, 255);
colors[1] = makecol(red2, green2, blue2, 255);
if (c1 > c2)
{
int blue3 = ((blue2 - blue1) >> 1) - ((blue2 - blue1) >> 3);
int green3 = ((green2 - green1) >> 1) - ((green2 - green1) >> 3);
int red3 = ((red2 - red1) >> 1) - ((red2 - red1) >> 3);
colors[2] = makecol(red1 + red3, green1 + green3, blue1 + blue3, 255);
colors[3] = makecol(red2 - red3, green2 - green3, blue2 - blue3, 255);
}
else
{
colors[2] = makecol((red1 + red2 + 1) / 2, // Average
(green1 + green2 + 1) / 2,
(blue1 + blue2 + 1) / 2, 255);
colors[3] = makecol(red2, green2, blue2, 0); // Color2 but transparent
}
for (int y = 0; y < 4; y++)
{
int val = src->lines[y];
for (int x = 0; x < 4; x++)
{
dst[x] = colors[(val >> 6) & 3];
val <<= 2;
}
dst += pitch;
}
}
void PSPSetTexture()
{
if (!tempArea)
tempArea = new u8[512*512*4*4]; // PSP maximum texture size
u32 texaddr = (gstate.texaddr[0] & 0xFFFFF0) | ((gstate.texbufwidth[0]<<8) & 0xFF000000);
texaddr &= 0xFFFFFFF;
if (!texaddr) return;
DEBUG_LOG(G3D,"Texture at %08x",texaddr);
u8 *texptr = Memory::GetPointer(texaddr);
TexCache::iterator iter = cache.find(texaddr);
if (iter != cache.end())
{
//Validate the texture here (width, height etc)
TexCacheEntry &entry = iter->second;
int dim = gstate.texsize[0] & 0xF0F;
if (dim == entry.dim && entry.hash == *(u32*)texptr)
{
//got one!
glBindTexture(GL_TEXTURE_2D, entry.texture);
UpdateSamplingParams();
DEBUG_LOG(G3D,"Texture at %08x Found in Cache, applying", texaddr);
return; //Done!
}
else
{
NOTICE_LOG(G3D,"Texture different or overwritten, reloading at %08x", texaddr);
//Damnit, got overwritten.
//if (dim != entry.dim)
//{
// glDeleteTextures(1, &entry.texture);
//}
cache.erase(iter);
}
}
else
{
NOTICE_LOG(G3D,"No texture in cache, decoding...");
}
//we have to decode it
TexCacheEntry entry;
entry.addr = texaddr;
entry.hash = *(u32*)texptr;
glGenTextures(1, &entry.texture);
NOTICE_LOG(G3D, "Creating texture %i", entry.texture);
glBindTexture(GL_TEXTURE_2D, entry.texture);
int bufw = gstate.texbufwidth[0] & 0x3ff;
entry.dim = gstate.texsize[0] & 0xF0F;
int w = 1 << (gstate.texsize[0] & 0xf);
int h = 1 << ((gstate.texsize[0]>>8) & 0xf);
gstate.curTextureHeight=h;
gstate.curTextureWidth=w;
int format = gstate.texformat & 0xF;
DEBUG_LOG(G3D,"Texture Width %04x Height %04x Bufw %d Fmt %d", w, h, bufw, format);
// TODO: Look into using BGRA for 32-bit textures when the GL_EXT_texture_format_BGRA8888 extension is available, as it's faster than RGBA on some chips.
switch (format)
{
case GE_TFMT_4444:
case GE_TFMT_5551:
case GE_TFMT_5650:
{
u16 *dst = (u16*)tempArea;
u16 *src = (u16*)texptr;
int fmt = GL_UNSIGNED_SHORT_5_6_5;
int internal_format = GL_RGBA;
u16 (*convFunc)(u16);
switch (format)
{
case GE_TFMT_4444: fmt = GL_UNSIGNED_SHORT_4_4_4_4; convFunc = &convert4444; break;
case GE_TFMT_5551: fmt = GL_UNSIGNED_SHORT_5_5_5_1; convFunc = &convert5551; break;
case GE_TFMT_5650: fmt = GL_UNSIGNED_SHORT_5_6_5; convFunc = &convert565; internal_format = GL_RGB; break;
}
if (gstate.texmode & 1) //Swizzled!
{
for (int y=0; y<h/8; y++)
{
for (int x=0; x<w/8; x++)
{
for (int yy=0; yy<8; yy++)
{
for (int xx=0; xx<8; xx++)
{
dst[(y*8+yy)*w + x*8 + xx] = convFunc(src[(y*(bufw/8)+x)*8 + (yy*8+xx)]);
}
}
}
}
}
else
{
for (int y=0; y<h; y++)
{
const u16 *s = src + bufw * y;
u16 *d = dst + y * w;
for (int x = 0; x < w; x++)
*d++ = convFunc(*s++);
}
}
// TODO: This will have to be redone for OpenGL ES 2.0.
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, internal_format, fmt, (GLvoid*)tempArea);
break;
}
case GE_TFMT_CLUT4:
{
u32 *dst = (u32*)tempArea;
u8 *src = (u8*)texptr;
if (gstate.texmode & 1) //Swizzled!
{
for (int y=0; y<h/8; y++)
{
for (int x=0; x<w/32; x++)
{
for (int yy=0; yy<8; yy++)
{
for (int xx=0; xx<32; xx++)
{
int idx = src[(y*bufw*4+x*16*8) + (yy*16+xx/2)];
if (xx&1) idx>>=4; else idx&=0xF;
dst[(y*8+yy)*w + x*32 + xx] = PaletteLoad(idx);
}
}
}
}
}
else
{
for (int y=0; y<h; y++)
{
for (int x=0; x<w; x++)
{
int idx = *src;
if (x&1) idx >>= 4; else idx &= 0xF;
dst[x] = PaletteLoad(idx);
if (x&1)
src++;
}
src -= w/2;
src += bufw/2;
dst += w;
}
}
int fmt = GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, fmt, (GLvoid*)tempArea);
break;
}
case GE_TFMT_CLUT8:
{
u32 *dst = (u32*)tempArea;
u8 *src = (u8*)texptr;
if (gstate.texmode & 1) //Swizzled!
{
for (int y=0; y<h/8; y++)
{
for (int x=0; x<w/16; x++)
{
for (int yy=0; yy<8; yy++)
{
for (int xx=0; xx<16; xx++)
{
int idx = src[(y*bufw*8+x*16*8) + (yy*16+xx)];
dst[(y*8+yy)*w + x*16 + xx] = PaletteLoad(idx);
}
}
}
}
}
else
{
for (int y=0; y<h; y++)
{
for (int x=0; x<w; x++)
{
int idx = src[x];
dst[x] = PaletteLoad(idx);
}
src += bufw;
dst += w;
}
}
int fmt = GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, fmt, (GLvoid*)tempArea);
break;
}
case GE_TFMT_8888:
{
u32 *dst = (u32*)tempArea;
u32 *src = (u32*)texptr;
if (gstate.texmode & 1) //Swizzled!
{
for (int y=0; y<h/8; y++)
{
int i = y*bufw*8;
for (int x=0; x<w/4; x++)
{
for (int yy=0; yy<8; yy++)
{
for (int xx=0; xx<4; xx++)
{
dst[(y*8+yy)*w + x*4 + xx] = src[i];
i++;
}
}
}
}
}
else
{
for (int y=0; y<h; y++)
{
memcpy(dst+y*w,src+bufw*y,4*bufw);
}
}
int fmt = GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, fmt, (GLvoid*)tempArea);
break;
}
case GE_TFMT_DXT1:
{
// THIS IS VERY BROKEN but can be debugged! :)
u32 *dst = (u32*)tempArea;
DXT1Block *src = (DXT1Block*)texptr;
for (int y=0; y<h/4; y++)
{
int i = y*w/4;
for (int x=0; x<w/4; x++)
{
decodeDXT1Block(dst + w*4 * y * 4 + x * 4, src + i, w);
i++;
}
}
int fmt = GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, fmt, (GLvoid*)tempArea);
break;
}
break;
case GE_TFMT_DXT3:
case GE_TFMT_DXT5:
default:
ERROR_LOG(G3D, "Unknown Texture Format %i, not setting texture",format);
PanicAlert("ANOTHER tex format??");
return;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glGenerateMipmap(GL_TEXTURE_2D);
UpdateSamplingParams();
cache[texaddr] = entry;
}