2012-11-01 16:19:01 +01: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 23:58:25 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01: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/.
# include <map>
2013-02-10 13:19:01 -08:00
# include <algorithm>
2012-11-01 16:19:01 +01:00
2013-02-21 21:37:19 +01:00
# include "Core/MemMap.h"
# include "GPU/ge_constants.h"
# include "GPU/GPUState.h"
# include "GPU/GLES/TextureCache.h"
# include "GPU/GLES/Framebuffer.h"
# include "Core/Config.h"
2012-11-01 16:19:01 +01:00
2013-02-15 00:30:02 +01:00
// If a texture hasn't been seen for this many frames, get rid of it.
2012-11-22 23:07:15 +01:00
# define TEXTURE_KILL_AGE 200
2013-02-15 00:30:02 +01:00
u32 RoundUpToPowerOf2 ( u32 v )
{
v - - ;
v | = v > > 1 ;
v | = v > > 2 ;
v | = v > > 4 ;
v | = v > > 8 ;
v | = v > > 16 ;
v + + ;
return v ;
}
2012-11-22 23:07:15 +01:00
2013-01-30 20:40:26 +01:00
TextureCache : : TextureCache ( ) {
2013-02-12 19:05:30 +01:00
lastBoundTexture = - 1 ;
2012-11-29 15:06:54 +01:00
// TODO: Switch to aligned allocations for alignment. AllocateMemoryPages would do the trick.
2013-01-11 02:00:51 +01:00
// This is 5MB of temporary storage. Might be possible to shrink it.
tmpTexBuf32 = new u32 [ 1024 * 512 ] ; // 2MB
tmpTexBuf16 = new u16 [ 1024 * 512 ] ; // 1MB
tmpTexBufRearrange = new u32 [ 1024 * 512 ] ; // 2MB
clutBuf32 = new u32 [ 4096 ] ; // 4K
clutBuf16 = new u16 [ 4096 ] ; // 4K
2013-02-11 20:48:07 +08:00
glGetFloatv ( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT , & maxAnisotropyLevel ) ;
2012-11-28 16:12:29 +01:00
}
2013-01-30 20:40:26 +01:00
TextureCache : : ~ TextureCache ( ) {
2012-11-28 16:12:29 +01:00
delete [ ] tmpTexBuf32 ;
tmpTexBuf32 = 0 ;
2012-12-01 02:12:30 +01:00
delete [ ] tmpTexBuf16 ;
tmpTexBuf16 = 0 ;
2012-11-28 16:12:29 +01:00
delete [ ] tmpTexBufRearrange ;
tmpTexBufRearrange = 0 ;
2012-11-29 15:06:54 +01:00
delete [ ] clutBuf32 ;
delete [ ] clutBuf16 ;
2012-11-28 16:12:29 +01:00
}
2013-01-30 20:40:26 +01:00
void TextureCache : : Clear ( bool delete_them ) {
2013-01-26 21:38:27 +01:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2013-01-06 12:11:47 +01:00
if ( delete_them ) {
for ( TexCache : : iterator iter = cache . begin ( ) ; iter ! = cache . end ( ) ; + + iter ) {
2012-11-01 16:19:01 +01:00
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 ( ) ;
}
}
2012-11-22 23:07:15 +01:00
// Removes old textures.
2013-01-30 20:40:26 +01:00
void TextureCache : : Decimate ( ) {
2013-01-26 21:38:27 +01:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
2013-02-25 00:59:23 -08:00
for ( TexCache : : iterator iter = cache . begin ( ) ; iter ! = cache . end ( ) ; ) {
2013-02-10 13:19:01 -08:00
if ( iter - > second . lastFrame + TEXTURE_KILL_AGE < gpuStats . numFrames ) {
2012-11-22 23:07:15 +01:00
glDeleteTextures ( 1 , & iter - > second . texture ) ;
2012-11-24 10:43:16 -08:00
cache . erase ( iter + + ) ;
2012-11-22 23:07:15 +01:00
}
2012-11-24 10:43:16 -08:00
else
+ + iter ;
2012-11-22 23:07:15 +01:00
}
}
2013-01-30 20:40:26 +01:00
void TextureCache : : Invalidate ( u32 addr , int size , bool force ) {
2013-01-22 19:18:48 +01:00
addr & = 0xFFFFFFF ;
2012-12-21 23:43:48 +01:00
u32 addr_end = addr + size ;
2013-02-10 14:14:20 -08:00
for ( TexCache : : iterator iter = cache . begin ( ) , end = cache . end ( ) ; iter ! = end ; + + iter ) {
2013-01-22 19:18:48 +01:00
u32 texAddr = iter - > second . addr ;
u32 texEnd = iter - > second . addr + iter - > second . sizeInRAM ;
2012-12-21 23:43:48 +01:00
// Clear if either the addr or clutaddr is in the range.
2013-01-22 19:18:48 +01:00
bool invalidate = ( texAddr > = addr & & texAddr < addr_end ) | | ( texEnd > = addr & & texEnd < addr_end ) ;
invalidate = invalidate | | ( addr > = texAddr & & addr < texEnd ) | | ( addr_end > = texAddr & & addr_end < texEnd ) ;
invalidate = invalidate | | ( iter - > second . clutaddr > = addr & & iter - > second . clutaddr < addr_end ) ;
2012-12-21 23:43:48 +01:00
2013-01-06 12:11:47 +01:00
if ( invalidate ) {
2013-02-10 14:11:53 -08:00
if ( iter - > second . status = = TexCacheEntry : : STATUS_RELIABLE ) {
iter - > second . status = TexCacheEntry : : STATUS_HASHING ;
}
2013-01-06 12:11:47 +01:00
if ( force ) {
2013-01-06 12:27:01 +01:00
gpuStats . numTextureInvalidations + + ;
2013-02-10 23:29:44 -08:00
// Start it over from 0.
iter - > second . numFrames = 0 ;
iter - > second . framesUntilNextFullHash = 0 ;
2013-01-06 12:11:47 +01:00
} else {
2013-01-05 17:04:56 -08:00
iter - > second . invalidHint + + ;
2013-01-02 23:21:02 -08:00
}
2012-12-21 23:43:48 +01:00
}
}
}
2013-01-30 20:40:26 +01:00
void TextureCache : : InvalidateAll ( bool force ) {
2013-02-10 14:14:20 -08:00
Invalidate ( 0 , 0xFFFFFFFF , force ) ;
2012-11-22 23:07:15 +01:00
}
2013-02-01 00:18:23 +01:00
TextureCache : : TexCacheEntry * TextureCache : : GetEntryAt ( u32 texaddr ) {
2013-02-09 21:18:46 +01:00
// If no CLUT, as in framebuffer textures, cache key is simply texaddr.
auto iter = cache . find ( texaddr ) ;
2013-02-09 21:32:02 +01:00
if ( iter ! = cache . end ( ) & & iter - > second . addr = = texaddr )
return & iter - > second ;
else
return 0 ;
2013-02-01 00:18:23 +01:00
}
2013-02-21 21:37:19 +01:00
void TextureCache : : NotifyFramebuffer ( u32 address , VirtualFramebuffer * framebuffer ) {
2013-02-09 21:18:46 +01:00
// Must be in VRAM so | 0x04000000 it is.
2013-02-01 00:18:23 +01:00
TexCacheEntry * entry = GetEntryAt ( address | 0x04000000 ) ;
if ( entry ) {
2013-02-12 21:52:19 +01:00
DEBUG_LOG ( HLE , " Render to texture detected at %08x! " , address ) ;
2013-02-21 21:37:19 +01:00
if ( ! entry - > framebuffer )
2013-03-03 13:00:21 +01:00
entry - > framebuffer = framebuffer ;
2013-02-09 21:18:46 +01:00
// TODO: Delete the original non-fbo texture too.
2013-02-01 00:18:23 +01:00
}
}
2013-02-21 21:37:19 +01:00
void TextureCache : : NotifyFramebufferDestroyed ( u32 address , VirtualFramebuffer * fbo ) {
2013-02-01 00:18:23 +01:00
TexCacheEntry * entry = GetEntryAt ( address | 0x04000000 ) ;
2013-02-21 21:37:19 +01:00
if ( entry & & entry - > framebuffer ) {
entry - > framebuffer = 0 ;
2013-02-01 00:18:23 +01:00
}
}
2013-01-30 20:40:26 +01:00
static u32 GetClutAddr ( u32 clutEntrySize ) {
2012-11-08 23:26:30 +01:00
return ( ( gstate . clutaddr & 0xFFFFFF ) | ( ( gstate . clutaddrupper < < 8 ) & 0x0F000000 ) ) + ( ( gstate . clutformat > > 16 ) & 0x1f ) * clutEntrySize ;
}
2013-01-30 20:40:26 +01:00
static u32 GetClutIndex ( u32 index ) {
2012-11-08 23:26:30 +01:00
return ( ( ( ( gstate . clutformat > > 16 ) & 0x1f ) + index ) > > ( ( gstate . clutformat > > 2 ) & 0x1f ) ) & ( ( gstate . clutformat > > 8 ) & 0xff ) ;
}
2013-01-30 20:40:26 +01:00
static void ReadClut16 ( u16 * clutBuf16 ) {
2012-11-08 23:26:30 +01:00
u32 clutNumEntries = ( gstate . loadclut & 0x3f ) * 16 ;
u32 clutAddr = GetClutAddr ( 2 ) ;
2013-01-06 12:11:47 +01:00
if ( Memory : : IsValidAddress ( clutAddr ) ) {
for ( u32 i = ( ( gstate . clutformat > > 16 ) & 0x1f ) ; i < clutNumEntries ; i + + )
clutBuf16 [ i ] = Memory : : ReadUnchecked_U16 ( clutAddr + i * 2 ) ;
}
2012-11-08 23:26:30 +01:00
}
2013-01-30 20:40:26 +01:00
static void ReadClut32 ( u32 * clutBuf32 ) {
2012-11-08 23:26:30 +01:00
u32 clutNumEntries = ( gstate . loadclut & 0x3f ) * 8 ;
u32 clutAddr = GetClutAddr ( 4 ) ;
2013-01-06 12:11:47 +01:00
if ( Memory : : IsValidAddress ( clutAddr ) ) {
for ( u32 i = ( ( gstate . clutformat > > 16 ) & 0x1f ) ; i < clutNumEntries ; i + + )
clutBuf32 [ i ] = Memory : : ReadUnchecked_U32 ( clutAddr + i * 4 ) ;
}
2012-11-08 23:26:30 +01:00
}
2012-11-01 16:19:01 +01:00
2013-01-30 20:40:26 +01:00
void * TextureCache : : UnswizzleFromMem ( u32 texaddr , u32 bytesPerPixel , u32 level ) {
2012-11-08 23:26:30 +01:00
u32 addr = texaddr ;
u32 rowWidth = ( bytesPerPixel > 0 ) ? ( ( gstate . texbufwidth [ level ] & 0x3FF ) * bytesPerPixel ) : ( ( gstate . texbufwidth [ level ] & 0x3FF ) / 2 ) ;
u32 pitch = rowWidth / 4 ;
2013-01-06 12:11:47 +01:00
int bxc = rowWidth / 16 ;
int byc = ( ( 1 < < ( ( gstate . texsize [ level ] > > 8 ) & 0xf ) ) + 7 ) / 8 ;
2012-11-08 23:26:30 +01:00
if ( byc = = 0 )
byc = 1 ;
u32 ydest = 0 ;
2013-01-06 12:11:47 +01:00
for ( int by = 0 ; by < byc ; by + + ) {
if ( rowWidth > = 16 ) {
2012-11-08 23:26:30 +01:00
u32 xdest = ydest ;
2013-01-06 12:11:47 +01:00
for ( int bx = 0 ; bx < bxc ; bx + + ) {
2012-11-08 23:26:30 +01:00
u32 dest = xdest ;
2013-01-06 12:11:47 +01:00
for ( int n = 0 ; n < 8 ; n + + ) {
for ( int k = 0 ; k < 4 ; k + + ) {
tmpTexBuf32 [ dest + k ] = Memory : : ReadUnchecked_U32 ( addr ) ;
2012-11-08 23:26:30 +01:00
addr + = 4 ;
}
dest + = pitch ;
}
xdest + = 4 ;
}
ydest + = ( rowWidth * 8 ) / 4 ;
2013-01-06 12:11:47 +01:00
} else if ( rowWidth = = 8 ) {
for ( int n = 0 ; n < 8 ; n + + , ydest + = 2 ) {
tmpTexBuf32 [ ydest + 0 ] = Memory : : ReadUnchecked_U32 ( addr + 0 ) ;
tmpTexBuf32 [ ydest + 1 ] = Memory : : ReadUnchecked_U32 ( addr + 4 ) ;
2012-11-08 23:26:30 +01:00
addr + = 16 ; // skip two u32
}
2013-01-06 12:11:47 +01:00
} else if ( rowWidth = = 4 ) {
for ( int n = 0 ; n < 8 ; n + + , ydest + + ) {
tmpTexBuf32 [ ydest ] = Memory : : ReadUnchecked_U32 ( addr ) ;
2012-11-08 23:26:30 +01:00
addr + = 16 ;
}
2013-01-06 12:11:47 +01:00
} else if ( rowWidth = = 2 ) {
for ( int n = 0 ; n < 4 ; n + + , ydest + + ) {
u16 n1 = Memory : : ReadUnchecked_U16 ( addr + 0 ) ;
u16 n2 = Memory : : ReadUnchecked_U16 ( addr + 16 ) ;
2012-11-08 23:26:30 +01:00
tmpTexBuf32 [ ydest ] = ( u32 ) n1 | ( ( u32 ) n2 < < 16 ) ;
addr + = 32 ;
}
}
2013-01-06 12:11:47 +01:00
else if ( rowWidth = = 1 ) {
for ( int n = 0 ; n < 2 ; n + + , ydest + + ) {
// This looks wrong, shouldn't it be & 0xFF (that is no mask at all?)
u8 n1 = Memory : : ReadUnchecked_U8 ( addr + 0 ) & 0xf ;
u8 n2 = Memory : : ReadUnchecked_U8 ( addr + 16 ) & 0xf ;
u8 n3 = Memory : : ReadUnchecked_U8 ( addr + 32 ) & 0xf ;
u8 n4 = Memory : : ReadUnchecked_U8 ( addr + 48 ) & 0xf ;
2012-11-08 23:26:30 +01:00
tmpTexBuf32 [ ydest ] = ( u32 ) n1 | ( ( u32 ) n2 < < 8 ) | ( ( u32 ) n3 < < 16 ) | ( ( u32 ) n4 < < 24 ) ;
}
}
}
return tmpTexBuf32 ;
}
2013-01-30 20:40:26 +01:00
void * TextureCache : : readIndexedTex ( int level , u32 texaddr , int bytesPerIndex ) {
2013-01-06 12:11:47 +01:00
int length = ( gstate . texbufwidth [ level ] & 0x3FF ) * ( 1 < < ( ( gstate . texsize [ level ] > > 8 ) & 0xf ) ) ;
2012-11-08 23:26:30 +01:00
void * buf = NULL ;
2013-01-06 12:11:47 +01:00
switch ( ( gstate . clutformat & 3 ) ) {
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_BGR5650 :
case GE_CMODE_16BIT_ABGR5551 :
case GE_CMODE_16BIT_ABGR4444 :
2012-11-01 16:19:01 +01:00
{
2013-01-30 20:40:26 +01:00
ReadClut16 ( clutBuf16 ) ;
const u16 * clut = clutBuf16 ;
2013-01-06 12:11:47 +01:00
if ( ! ( gstate . texmode & 1 ) ) {
switch ( bytesPerIndex ) {
2012-11-08 23:26:30 +01:00
case 1 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 ; i < length ; i + + ) {
u8 index = Memory : : ReadUnchecked_U8 ( texaddr + i ) ;
2012-11-08 23:26:30 +01:00
tmpTexBuf16 [ i ] = clut [ GetClutIndex ( index ) ] ;
}
break ;
case 2 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 ; i < length ; i + + ) {
u16 index = Memory : : ReadUnchecked_U16 ( texaddr + i * 2 ) ;
2012-11-08 23:26:30 +01:00
tmpTexBuf16 [ i ] = clut [ GetClutIndex ( index ) ] ;
}
break ;
case 4 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 ; i < length ; i + + ) {
u32 index = Memory : : ReadUnchecked_U32 ( texaddr + i * 4 ) ;
2012-11-08 23:26:30 +01:00
tmpTexBuf16 [ i ] = clut [ GetClutIndex ( index ) ] ;
}
break ;
}
2013-01-06 12:11:47 +01:00
} else {
2012-11-08 23:26:30 +01:00
UnswizzleFromMem ( texaddr , bytesPerIndex , level ) ;
2013-01-06 12:11:47 +01:00
switch ( bytesPerIndex ) {
2012-11-08 23:26:30 +01:00
case 1 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 , j = 0 ; i < length ; i + = 4 , j + + ) {
2012-11-08 23:26:30 +01:00
u32 n = tmpTexBuf32 [ j ] ;
u32 k ;
for ( k = 0 ; k < 4 ; k + + ) {
2012-11-18 18:47:29 +01:00
u8 index = ( n > > ( k * 8 ) ) & 0xff ;
2012-11-08 23:26:30 +01:00
tmpTexBuf16 [ i + k ] = clut [ GetClutIndex ( index ) ] ;
}
}
break ;
case 2 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 , j = 0 ; i < length ; i + = 2 , j + + ) {
2012-11-08 23:26:30 +01:00
u32 n = tmpTexBuf32 [ j ] ;
tmpTexBuf16 [ i + 0 ] = clut [ GetClutIndex ( n & 0xffff ) ] ;
tmpTexBuf16 [ i + 1 ] = clut [ GetClutIndex ( n > > 16 ) ] ;
}
break ;
case 4 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 ; i < length ; i + + ) {
2012-11-08 23:26:30 +01:00
u32 n = tmpTexBuf32 [ i ] ;
tmpTexBuf16 [ i ] = clut [ GetClutIndex ( n ) ] ;
}
break ;
}
}
buf = tmpTexBuf16 ;
}
break ;
case GE_CMODE_32BIT_ABGR8888 :
{
2013-01-30 20:40:26 +01:00
ReadClut32 ( clutBuf32 ) ;
const u32 * clut = clutBuf32 ;
2013-01-06 12:11:47 +01:00
if ( ! ( gstate . texmode & 1 ) ) {
switch ( bytesPerIndex ) {
2012-11-08 23:26:30 +01:00
case 1 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 ; i < length ; i + + ) {
u8 index = Memory : : ReadUnchecked_U8 ( texaddr + i ) ;
2012-11-08 23:26:30 +01:00
tmpTexBuf32 [ i ] = clut [ GetClutIndex ( index ) ] ;
}
break ;
case 2 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 ; i < length ; i + + ) {
u16 index = Memory : : ReadUnchecked_U16 ( texaddr + i * 2 ) ;
2012-11-08 23:26:30 +01:00
tmpTexBuf32 [ i ] = clut [ GetClutIndex ( index ) ] ;
}
break ;
case 4 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 ; i < length ; i + + ) {
u32 index = Memory : : ReadUnchecked_U32 ( texaddr + i * 4 ) ;
2012-11-08 23:26:30 +01:00
tmpTexBuf32 [ i ] = clut [ GetClutIndex ( index ) ] ;
}
break ;
}
2013-01-06 12:11:47 +01:00
} else {
2012-11-08 23:26:30 +01:00
UnswizzleFromMem ( texaddr , bytesPerIndex , level ) ;
2013-01-06 12:11:47 +01:00
switch ( bytesPerIndex ) {
2012-11-08 23:26:30 +01:00
case 1 :
2013-01-06 12:11:47 +01:00
for ( int i = length - 4 , j = ( length / 4 ) - 1 ; i > = 0 ; i - = 4 , j - - ) {
2012-11-08 23:26:30 +01:00
u32 n = tmpTexBuf32 [ j ] ;
u32 k ;
for ( k = 0 ; k < 4 ; k + + ) {
u32 index = ( n > > ( k * 8 ) ) & 0xff ;
tmpTexBuf32 [ i + k ] = clut [ GetClutIndex ( index ) ] ;
}
}
break ;
case 2 :
2013-01-06 12:11:47 +01:00
for ( int i = length - 2 , j = ( length / 2 ) - 1 ; i > = 0 ; i - = 2 , j - - ) {
2012-11-08 23:26:30 +01:00
u32 n = tmpTexBuf32 [ j ] ;
tmpTexBuf32 [ i + 0 ] = clut [ GetClutIndex ( n & 0xffff ) ] ;
tmpTexBuf32 [ i + 1 ] = clut [ GetClutIndex ( n > > 16 ) ] ;
}
break ;
case 4 :
2013-01-06 12:11:47 +01:00
for ( int i = 0 ; i < length ; i + + ) {
2012-11-08 23:26:30 +01:00
u32 n = tmpTexBuf32 [ i ] ;
tmpTexBuf32 [ i ] = clut [ GetClutIndex ( n ) ] ;
}
break ;
}
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
buf = tmpTexBuf32 ;
}
break ;
2012-11-01 16:19:01 +01:00
2012-11-08 23:26:30 +01:00
default :
ERROR_LOG ( G3D , " Unhandled clut texture mode %d!!! " , ( gstate . clutformat & 3 ) ) ;
break ;
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
return buf ;
}
2013-01-06 12:11:47 +01:00
GLenum getClutDestFormat ( GEPaletteFormat format ) {
switch ( format ) {
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_ABGR4444 :
2012-11-09 01:24:19 +01:00
return GL_UNSIGNED_SHORT_4_4_4_4 ;
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_ABGR5551 :
2012-11-09 01:24:19 +01:00
return GL_UNSIGNED_SHORT_5_5_5_1 ;
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_BGR5650 :
2012-11-09 01:24:19 +01:00
return GL_UNSIGNED_SHORT_5_6_5 ;
2012-11-08 23:26:30 +01:00
case GE_CMODE_32BIT_ABGR8888 :
return GL_UNSIGNED_BYTE ;
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2013-02-08 00:04:34 +01:00
static const u8 texByteAlignMap [ ] = { 2 , 2 , 2 , 4 } ;
2012-11-08 23:26:30 +01:00
2013-02-08 00:04:34 +01:00
static const GLuint MinFiltGL [ 8 ] = {
2013-01-06 17:44:14 +01:00
GL_NEAREST ,
GL_LINEAR ,
GL_NEAREST ,
GL_LINEAR ,
GL_NEAREST_MIPMAP_NEAREST ,
GL_LINEAR_MIPMAP_NEAREST ,
GL_NEAREST_MIPMAP_LINEAR ,
GL_LINEAR_MIPMAP_LINEAR ,
} ;
2013-02-08 00:04:34 +01:00
static const GLuint MagFiltGL [ 2 ] = {
2013-01-06 17:44:14 +01:00
GL_NEAREST ,
GL_LINEAR
} ;
2013-02-10 12:13:35 +01:00
// OpenGL ES 2.0 workaround. This SHOULD be available but is NOT in the headers in Android.
// Let's see if this hackery works.
2013-02-08 00:04:34 +01:00
# ifndef GL_TEXTURE_LOD_BIAS
# define GL_TEXTURE_LOD_BIAS 0x8501
# endif
2013-02-10 12:13:35 +01:00
# ifndef GL_TEXTURE_MAX_LOD
# define GL_TEXTURE_MAX_LOD 0x813B
# endif
2012-11-01 16:19:01 +01:00
// This should not have to be done per texture! OpenGL is silly yo
// TODO: Dirty-check this against the current texture.
2013-01-30 20:40:26 +01:00
void TextureCache : : UpdateSamplingParams ( TexCacheEntry & entry , bool force ) {
2012-11-01 16:19:01 +01:00
int minFilt = gstate . texfilter & 0x7 ;
2013-01-06 12:11:47 +01:00
int magFilt = ( gstate . texfilter > > 8 ) & 1 ;
2013-01-06 17:44:14 +01:00
bool sClamp = gstate . texwrap & 1 ;
bool tClamp = ( gstate . texwrap > > 8 ) & 1 ;
2013-02-08 00:04:34 +01:00
if ( entry . maxLevel = = 0 ) {
// Enforce no mip filtering, for safety.
minFilt & = 1 ; // no mipmaps yet
} else {
// TODO: Is this a signed value? Which direction?
float lodBias = 0.0 ; // -(float)((gstate.texlevel >> 16) & 0xFF) / 16.0f;
if ( force | | entry . lodBias ! = lodBias ) {
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_LOD_BIAS , lodBias ) ;
entry . lodBias = lodBias ;
}
}
2012-11-01 16:19:01 +01:00
2013-01-06 12:11:47 +01:00
if ( g_Config . bLinearFiltering ) {
2013-01-06 17:44:14 +01:00
magFilt | = 1 ;
minFilt | = 1 ;
}
if ( force | | entry . minFilt ! = minFilt ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , MinFiltGL [ minFilt ] ) ;
entry . minFilt = minFilt ;
}
if ( force | | entry . magFilt ! = magFilt ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , MagFiltGL [ magFilt ] ) ;
entry . magFilt = magFilt ;
}
if ( force | | entry . sClamp ! = sClamp ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT ) ;
entry . sClamp = sClamp ;
}
if ( force | | entry . tClamp ! = tClamp ) {
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT ) ;
entry . tClamp = tClamp ;
2012-12-28 23:23:42 +08:00
}
2012-11-01 16:19:01 +01:00
}
2012-11-29 15:06:54 +01:00
// All these DXT structs are in the reverse order, as compared to PC.
// On PC, alpha comes before color, and interpolants are before the tile data.
2013-01-06 12:11:47 +01:00
struct DXT1Block {
2012-11-28 16:12:29 +01:00
u8 lines [ 4 ] ;
2012-11-01 16:19:01 +01:00
u16 color1 ;
u16 color2 ;
2012-11-28 16:12:29 +01:00
} ;
2013-01-06 12:11:47 +01:00
struct DXT3Block {
2012-11-28 16:12:29 +01:00
DXT1Block color ;
u16 alphaLines [ 4 ] ;
} ;
2013-01-06 12:11:47 +01:00
struct DXT5Block {
2012-11-28 16:12:29 +01:00
DXT1Block color ;
u32 alphadata2 ;
u16 alphadata1 ;
u8 alpha1 ; u8 alpha2 ;
2012-11-01 16:19:01 +01:00
} ;
2013-01-30 20:40:26 +01:00
static inline u32 makecol ( int r , int g , int b , int a ) {
2013-02-08 00:04:34 +01:00
return ( a < < 24 ) | ( r < < 16 ) | ( g < < 8 ) | b ;
2012-11-01 16:19:01 +01:00
}
2012-11-29 15:06:54 +01:00
// This could probably be done faster by decoding two or four blocks at a time with SSE/NEON.
2013-01-30 20:40:26 +01:00
static void decodeDXT1Block ( u32 * dst , const DXT1Block * src , int pitch , bool ignore1bitAlpha = false ) {
2012-11-01 16:19:01 +01:00
// S3TC Decoder
// Needs more speed and debugging.
2012-11-28 16:12:29 +01:00
u16 c1 = ( src - > color1 ) ;
u16 c2 = ( src - > color2 ) ;
int red1 = Convert5To8 ( c1 & 0x1F ) ;
int red2 = Convert5To8 ( c2 & 0x1F ) ;
2012-11-01 16:19:01 +01:00
int green1 = Convert6To8 ( ( c1 > > 5 ) & 0x3F ) ;
int green2 = Convert6To8 ( ( c2 > > 5 ) & 0x3F ) ;
2012-11-28 16:12:29 +01:00
int blue1 = Convert5To8 ( ( c1 > > 11 ) & 0x1F ) ;
int blue2 = Convert5To8 ( ( c2 > > 11 ) & 0x1F ) ;
2013-01-06 12:11:47 +01:00
2012-11-28 16:12:29 +01:00
u32 colors [ 4 ] ;
2012-11-01 16:19:01 +01:00
colors [ 0 ] = makecol ( red1 , green1 , blue1 , 255 ) ;
colors [ 1 ] = makecol ( red2 , green2 , blue2 , 255 ) ;
2013-01-06 12:11:47 +01:00
if ( c1 > c2 | | ignore1bitAlpha ) {
2012-11-01 16:19:01 +01:00
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 ) ;
2012-11-08 23:26:30 +01:00
colors [ 3 ] = makecol ( red2 - red3 , green2 - green3 , blue2 - blue3 , 255 ) ;
2013-01-06 12:11:47 +01:00
} else {
2012-11-01 16:19:01 +01:00
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
}
2013-01-06 12:11:47 +01:00
for ( int y = 0 ; y < 4 ; y + + ) {
2012-11-01 16:19:01 +01:00
int val = src - > lines [ y ] ;
2013-01-06 12:11:47 +01:00
for ( int x = 0 ; x < 4 ; x + + ) {
2012-11-28 16:12:29 +01:00
dst [ x ] = colors [ val & 3 ] ;
val > > = 2 ;
}
dst + = pitch ;
}
}
2013-01-30 20:40:26 +01:00
static void decodeDXT3Block ( u32 * dst , const DXT3Block * src , int pitch )
2012-11-28 16:12:29 +01:00
{
decodeDXT1Block ( dst , & src - > color , pitch , true ) ;
// Alpha: TODO
}
2013-01-30 20:40:26 +01:00
static inline u8 lerp8 ( const DXT5Block * src , int n ) {
2012-11-28 16:12:29 +01:00
float d = n / 7.0f ;
return ( u8 ) ( src - > alpha1 + ( src - > alpha2 - src - > alpha1 ) * d ) ;
}
2013-01-30 20:40:26 +01:00
static inline u8 lerp6 ( const DXT5Block * src , int n ) {
2012-11-28 16:12:29 +01:00
float d = n / 5.0f ;
return ( u8 ) ( src - > alpha1 + ( src - > alpha2 - src - > alpha1 ) * d ) ;
}
// The alpha channel is not 100% correct
2013-01-30 20:40:26 +01:00
static void decodeDXT5Block ( u32 * dst , const DXT5Block * src , int pitch ) {
2012-11-28 16:12:29 +01:00
decodeDXT1Block ( dst , & src - > color , pitch , true ) ;
u8 alpha [ 8 ] ;
alpha [ 0 ] = src - > alpha1 ;
alpha [ 1 ] = src - > alpha2 ;
if ( alpha [ 0 ] > alpha [ 1 ] ) {
alpha [ 2 ] = lerp8 ( src , 6 ) ;
alpha [ 3 ] = lerp8 ( src , 5 ) ;
alpha [ 4 ] = lerp8 ( src , 4 ) ;
alpha [ 5 ] = lerp8 ( src , 3 ) ;
alpha [ 6 ] = lerp8 ( src , 2 ) ;
alpha [ 7 ] = lerp8 ( src , 1 ) ;
} else {
alpha [ 2 ] = lerp6 ( src , 4 ) ;
alpha [ 3 ] = lerp6 ( src , 3 ) ;
alpha [ 4 ] = lerp6 ( src , 2 ) ;
alpha [ 5 ] = lerp6 ( src , 1 ) ;
alpha [ 6 ] = 0 ;
alpha [ 7 ] = 255 ;
}
u64 data = ( ( u64 ) src - > alphadata1 < < 32 ) | src - > alphadata2 ;
2013-01-06 12:11:47 +01:00
for ( int y = 0 ; y < 4 ; y + + ) {
for ( int x = 0 ; x < 4 ; x + + ) {
2012-11-28 16:12:29 +01:00
dst [ x ] = ( dst [ x ] & 0xFFFFFF ) | ( alpha [ data & 7 ] < < 24 ) ;
data > > = 3 ;
2012-11-01 16:19:01 +01:00
}
dst + = pitch ;
}
}
2013-01-30 20:40:26 +01:00
static void convertColors ( u8 * finalBuf , GLuint dstFmt , int numPixels ) {
2013-02-12 19:05:30 +01:00
// TODO: All these can be further sped up with SSE or NEON.
2012-11-09 01:24:19 +01:00
switch ( dstFmt ) {
case GL_UNSIGNED_SHORT_4_4_4_4 :
{
2013-02-12 19:05:30 +01:00
u32 * p = ( u32 * ) finalBuf ;
for ( int i = 0 ; i < ( numPixels + 1 ) / 2 ; i + + ) {
u32 c = p [ i ] ;
p [ i ] = ( ( c > > 12 ) & 0x000F000F ) |
( ( c > > 4 ) & 0x00F000F0 ) |
( ( c < < 4 ) & 0x0F000F00 ) |
( ( c < < 12 ) & 0xF000F000 ) ;
2012-11-09 01:24:19 +01:00
}
}
break ;
case GL_UNSIGNED_SHORT_5_5_5_1 :
{
2013-02-12 19:05:30 +01:00
u32 * p = ( u32 * ) finalBuf ;
for ( int i = 0 ; i < ( numPixels + 1 ) / 2 ; i + + ) {
u32 c = p [ i ] ;
p [ i ] = ( ( c > > 15 ) & 0x00010001 ) |
( ( c > > 9 ) & 0x003E003E ) |
( ( c < < 1 ) & 0x07C007C0 ) |
( ( c < < 11 ) & 0xF800F800 ) ;
2012-11-09 01:24:19 +01:00
}
}
break ;
case GL_UNSIGNED_SHORT_5_6_5 :
{
2013-02-12 19:05:30 +01:00
u32 * p = ( u32 * ) finalBuf ;
for ( int i = 0 ; i < ( numPixels + 1 ) / 2 ; i + + ) {
u32 c = p [ i ] ;
p [ i ] = ( ( c > > 11 ) & 0x001F001F ) |
( c & 0x07E007E0 ) |
( ( c < < 11 ) & 0xF800F800 ) ;
2012-11-09 01:24:19 +01:00
}
}
break ;
default :
{
2012-11-29 15:06:54 +01:00
// No need to convert RGBA8888, right order already
2012-11-09 01:24:19 +01:00
}
break ;
}
}
2013-01-30 20:40:26 +01:00
void TextureCache : : StartFrame ( ) {
2013-01-10 23:49:33 +01:00
lastBoundTexture = - 1 ;
2013-01-30 20:40:26 +01:00
Decimate ( ) ;
2013-01-10 23:49:33 +01:00
}
2013-01-30 20:40:26 +01:00
static const u8 bitsPerPixel [ 11 ] = {
2013-01-22 19:18:48 +01:00
16 , //GE_TFMT_5650=16,
16 , //GE_TFMT_5551=16,
16 , //GE_TFMT_4444=16,
32 , //GE_TFMT_8888=3,
4 , //GE_TFMT_CLUT4=4,
8 , //GE_TFMT_CLUT8=5,
16 , //GE_TFMT_CLUT16=6,
32 , //GE_TFMT_CLUT32=7,
4 , //GE_TFMT_DXT1=4,
8 , //GE_TFMT_DXT3=8,
8 , //GE_TFMT_DXT5=8,
} ;
2013-02-01 00:02:50 +01:00
static const bool formatUsesClut [ 11 ] = {
false ,
false ,
false ,
false ,
true ,
true ,
true ,
true ,
false ,
false ,
false ,
} ;
2013-01-22 19:18:48 +01:00
2013-02-10 13:37:05 -08:00
static inline u32 MiniHash ( const u32 * ptr ) {
return ptr [ 0 ] ;
}
static inline u32 QuickTexHash ( u32 addr , int bufw , int w , int h , u32 format ) {
2013-02-12 07:55:44 -08:00
u32 sizeInRAM = ( bitsPerPixel [ format < 11 ? format : 0 ] * bufw * h / 2 ) / 8 ;
2013-02-10 13:37:05 -08:00
const u32 * checkp = ( const u32 * ) Memory : : GetPointer ( addr ) ;
u32 check = 0 ;
2013-02-12 07:55:44 -08:00
for ( u32 i = 0 ; i < ( sizeInRAM * 2 ) / 4 ; + + i )
check + = * checkp + + ;
2013-02-10 13:37:05 -08:00
return check ;
}
2013-01-30 20:40:26 +01:00
void TextureCache : : SetTexture ( ) {
2013-01-22 19:18:48 +01:00
u32 texaddr = ( gstate . texaddr [ 0 ] & 0xFFFFF0 ) | ( ( gstate . texbufwidth [ 0 ] < < 8 ) & 0x0F000000 ) ;
2013-01-06 12:11:47 +01:00
if ( ! Memory : : IsValidAddress ( texaddr ) ) {
// Bind a null texture and return.
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
return ;
}
2012-11-18 23:35:02 +01:00
u32 format = gstate . texformat & 0xF ;
2013-02-01 00:02:50 +01:00
if ( format > = 11 ) {
ERROR_LOG ( G3D , " Unknown texture format %i " , format ) ;
format = 0 ;
}
2013-02-01 00:18:23 +01:00
bool hasClut = formatUsesClut [ format ] ;
2013-02-01 00:02:50 +01:00
2013-02-09 21:18:46 +01:00
u64 cachekey = texaddr ;
2013-02-12 21:00:51 +01:00
u32 clutformat , clutaddr ;
if ( hasClut ) {
clutformat = gstate . clutformat & 3 ;
clutaddr = GetClutAddr ( clutformat = = GE_CMODE_32BIT_ABGR8888 ? 4 : 2 ) ;
2013-02-09 19:24:48 +01:00
cachekey | = ( u64 ) clutaddr < < 32 ;
2013-02-18 08:34:51 -08:00
} else {
clutaddr = 0 ;
2013-02-12 21:00:51 +01:00
}
2012-11-01 16:19:01 +01:00
2013-02-09 19:24:48 +01:00
int maxLevel = ( ( gstate . texmode > > 16 ) & 0x7 ) ;
2013-02-01 00:02:50 +01:00
2013-02-09 21:18:46 +01:00
u32 texhash = MiniHash ( ( const u32 * ) Memory : : GetPointer ( texaddr ) ) ;
2013-02-02 12:37:41 +01:00
2012-11-24 10:58:10 -08:00
TexCache : : iterator iter = cache . find ( cachekey ) ;
2013-02-10 14:11:53 -08:00
TexCacheEntry * entry = NULL ;
2013-02-15 00:30:02 +01:00
gstate_c . flipTexture = false ;
2013-03-03 13:00:21 +01:00
gstate_c . skipDrawReason & = ~ SKIPDRAW_BAD_FB_TEXTURE ;
2013-02-15 00:30:02 +01:00
2013-01-06 12:11:47 +01:00
if ( iter ! = cache . end ( ) ) {
2013-02-10 14:11:53 -08:00
entry = & iter - > second ;
2013-02-09 21:18:46 +01:00
// Check for FBO - slow!
2013-03-11 11:05:52 +10:00
if ( entry - > framebuffer ) {
2013-03-03 13:00:21 +01:00
entry - > framebuffer - > usageFlags | = FB_USAGE_TEXTURE ;
2013-03-11 18:24:03 +01:00
if ( entry - > framebuffer - > fbo ) {
if ( ! g_Config . bBufferedRendering ) {
ERROR_LOG ( HLE , " Buffered rendering is off! How did we end up trying to set an fbo as texture? fbo = %p " , entry - > framebuffer - > fbo ) ;
} else {
fbo_bind_color_as_texture ( entry - > framebuffer - > fbo , 0 ) ;
}
2013-03-08 22:51:04 +08:00
} else {
2013-03-03 13:00:21 +01:00
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
gstate_c . skipDrawReason | = SKIPDRAW_BAD_FB_TEXTURE ;
}
2013-02-10 14:11:53 -08:00
UpdateSamplingParams ( * entry , false ) ;
2013-02-09 21:18:46 +01:00
2013-02-15 00:30:02 +01:00
// This isn't right.
2013-02-21 21:37:19 +01:00
gstate_c . curTextureWidth = entry - > framebuffer - > width ;
gstate_c . curTextureHeight = entry - > framebuffer - > height ;
int h = 1 < < ( ( gstate . texsize [ 0 ] > > 8 ) & 0xf ) ;
gstate_c . actualTextureHeight = h ;
2013-02-15 00:30:02 +01:00
gstate_c . flipTexture = true ;
entry - > lastFrame = gpuStats . numFrames ;
2013-02-09 21:18:46 +01:00
return ;
}
//Validate the texture here (width, height etc)
2012-11-01 16:19:01 +01:00
int dim = gstate . texsize [ 0 ] & 0xF0F ;
2012-11-10 11:44:30 +01:00
bool match = true ;
2013-02-10 14:11:53 -08:00
bool rehash = entry - > status = = TexCacheEntry : : STATUS_UNRELIABLE ;
2012-11-01 16:19:01 +01:00
2012-11-10 11:44:30 +01:00
//TODO: Check more texture parameters, compute real texture hash
2013-02-12 21:00:51 +01:00
if ( dim ! = entry - > dim | |
entry - > hash ! = texhash | |
entry - > format ! = format | |
entry - > maxLevel ! = maxLevel | |
2013-02-18 08:34:51 -08:00
( hasClut & &
2013-02-12 21:00:51 +01:00
( entry - > clutformat ! = clutformat | |
2013-02-10 14:11:53 -08:00
entry - > clutaddr ! = clutaddr | |
2013-02-12 21:00:51 +01:00
entry - > cluthash ! = Memory : : Read_U32 ( entry - > clutaddr ) ) ) )
2013-02-10 13:19:01 -08:00
match = false ;
if ( match ) {
2013-02-10 14:11:53 -08:00
if ( entry - > lastFrame ! = gpuStats . numFrames ) {
entry - > numFrames + + ;
2013-02-10 13:19:01 -08:00
}
2013-02-10 14:11:53 -08:00
if ( entry - > framesUntilNextFullHash = = 0 ) {
2013-02-10 13:19:01 -08:00
// Exponential backoff up to 2048 frames. Textures are often reused.
2013-02-10 14:11:53 -08:00
entry - > framesUntilNextFullHash = std : : min ( 2048 , entry - > numFrames ) ;
rehash = true ;
2013-02-10 13:19:01 -08:00
} else {
2013-02-10 14:11:53 -08:00
- - entry - > framesUntilNextFullHash ;
2013-02-10 13:19:01 -08:00
}
}
2013-01-05 17:04:56 -08:00
// If it's not huge or has been invalidated many times, recheck the whole texture.
2013-02-10 14:11:53 -08:00
if ( entry - > invalidHint > 180 | | ( entry - > invalidHint > 15 & & dim < = 0x909 ) ) {
entry - > invalidHint = 0 ;
rehash = true ;
}
2013-01-02 23:21:02 -08:00
2013-02-10 14:11:53 -08:00
if ( rehash & & entry - > status ! = TexCacheEntry : : STATUS_RELIABLE ) {
2013-02-12 21:00:51 +01:00
int w = 1 < < ( gstate . texsize [ 0 ] & 0xf ) ;
int h = 1 < < ( ( gstate . texsize [ 0 ] > > 8 ) & 0xf ) ;
int bufw = gstate . texbufwidth [ 0 ] & 0x3ff ;
2013-02-10 13:37:05 -08:00
u32 check = QuickTexHash ( texaddr , bufw , w , h , format ) ;
2013-02-10 14:11:53 -08:00
if ( check ! = entry - > fullhash ) {
2013-01-02 23:21:02 -08:00
match = false ;
2013-02-10 14:14:20 -08:00
gpuStats . numTextureInvalidations + + ;
2013-02-10 14:11:53 -08:00
entry - > status = TexCacheEntry : : STATUS_UNRELIABLE ;
2013-02-10 23:29:44 -08:00
entry - > numFrames = 0 ;
} else if ( entry - > status = = TexCacheEntry : : STATUS_UNRELIABLE & & entry - > numFrames > TexCacheEntry : : FRAMES_REGAIN_TRUST ) {
entry - > status = TexCacheEntry : : STATUS_HASHING ;
2013-01-02 23:21:02 -08:00
}
}
2012-11-18 23:35:02 +01:00
if ( match ) {
2013-02-10 14:11:53 -08:00
// TODO: Mark the entry reliable if it's been safe for long enough?
2012-11-01 16:19:01 +01:00
//got one!
2013-02-10 14:11:53 -08:00
entry - > lastFrame = gpuStats . numFrames ;
if ( entry - > texture ! = lastBoundTexture ) {
glBindTexture ( GL_TEXTURE_2D , entry - > texture ) ;
lastBoundTexture = entry - > texture ;
2013-01-10 23:49:33 +01:00
}
2013-02-10 14:11:53 -08:00
UpdateSamplingParams ( * entry , false ) ;
2012-11-22 23:07:15 +01:00
DEBUG_LOG ( G3D , " Texture at %08x Found in Cache, applying " , texaddr ) ;
2012-11-01 16:19:01 +01:00
return ; //Done!
2012-11-18 23:35:02 +01:00
} else {
2012-11-22 23:07:15 +01:00
INFO_LOG ( G3D , " Texture different or overwritten, reloading at %08x " , texaddr ) ;
2013-02-12 20:12:08 +01:00
if ( entry - > texture = = lastBoundTexture )
lastBoundTexture = - 1 ;
2013-02-10 14:11:53 -08:00
glDeleteTextures ( 1 , & entry - > texture ) ;
2013-02-10 14:25:44 -08:00
if ( entry - > status = = TexCacheEntry : : STATUS_RELIABLE ) {
entry - > status = TexCacheEntry : : STATUS_HASHING ;
}
2012-11-01 16:19:01 +01:00
}
2013-01-06 12:11:47 +01:00
} else {
2012-11-22 23:07:15 +01:00
INFO_LOG ( G3D , " No texture in cache, decoding... " ) ;
2013-02-10 14:11:53 -08:00
TexCacheEntry entryNew = { 0 } ;
cache [ cachekey ] = entryNew ;
2013-02-10 14:25:44 -08:00
2013-02-10 14:11:53 -08:00
entry = & cache [ cachekey ] ;
2013-02-10 14:25:44 -08:00
entry - > status = TexCacheEntry : : STATUS_HASHING ;
2012-11-01 16:19:01 +01:00
}
2013-02-12 21:00:51 +01:00
int w = 1 < < ( gstate . texsize [ 0 ] & 0xf ) ;
int h = 1 < < ( ( gstate . texsize [ 0 ] > > 8 ) & 0xf ) ;
int bufw = gstate . texbufwidth [ 0 ] & 0x3ff ;
2012-11-01 16:19:01 +01:00
//we have to decode it
2013-02-10 14:11:53 -08:00
entry - > addr = texaddr ;
entry - > hash = texhash ;
entry - > format = format ;
entry - > lastFrame = gpuStats . numFrames ;
2013-02-21 21:37:19 +01:00
entry - > framebuffer = 0 ;
2013-02-10 14:11:53 -08:00
entry - > maxLevel = maxLevel ;
entry - > lodBias = 0.0f ;
2012-11-10 11:44:30 +01:00
2013-02-12 21:00:51 +01:00
2013-02-18 08:34:51 -08:00
if ( hasClut ) {
2013-02-10 14:11:53 -08:00
entry - > clutformat = clutformat ;
2013-02-18 08:34:51 -08:00
entry - > clutaddr = clutaddr ;
2013-02-10 14:11:53 -08:00
entry - > cluthash = Memory : : Read_U32 ( entry - > clutaddr ) ;
2013-01-06 12:11:47 +01:00
} else {
2013-02-10 14:11:53 -08:00
entry - > clutaddr = 0 ;
2012-11-10 11:44:30 +01:00
}
2012-11-01 16:19:01 +01:00
2013-02-10 14:11:53 -08:00
entry - > dim = gstate . texsize [ 0 ] & 0xF0F ;
2012-11-01 16:19:01 +01:00
2013-01-22 19:18:48 +01:00
// This would overestimate the size in many case so we underestimate instead
// to avoid excessive clearing caused by cache invalidations.
2013-02-10 14:11:53 -08:00
entry - > sizeInRAM = ( bitsPerPixel [ format < 11 ? format : 0 ] * bufw * h / 2 ) / 8 ;
2013-01-22 19:18:48 +01:00
2013-02-10 14:11:53 -08:00
entry - > fullhash = QuickTexHash ( texaddr , bufw , w , h , format ) ;
2013-01-02 23:21:02 -08:00
2013-02-08 00:04:34 +01:00
gstate_c . curTextureWidth = w ;
gstate_c . curTextureHeight = h ;
2013-02-10 14:11:53 -08:00
glGenTextures ( 1 , & entry - > texture ) ;
glBindTexture ( GL_TEXTURE_2D , entry - > texture ) ;
2013-02-12 20:12:08 +01:00
lastBoundTexture = entry - > texture ;
2013-02-08 00:04:34 +01:00
2013-02-12 21:00:51 +01:00
// Adjust maxLevel to actually present levels..
for ( int i = 0 ; i < = maxLevel ; i + + ) {
// If encountering levels pointing to nothing, adjust max level.
u32 levelTexaddr = ( gstate . texaddr [ i ] & 0xFFFFF0 ) | ( ( gstate . texbufwidth [ i ] < < 8 ) & 0x0F000000 ) ;
if ( ! Memory : : IsValidAddress ( levelTexaddr ) ) {
maxLevel = i - 1 ;
break ;
}
}
2013-02-09 20:21:10 +01:00
# ifdef USING_GLES2
// GLES2 doesn't have support for a "Max lod" which is critical as PSP games often
// don't specify mips all the way down. As a result, we either need to manually generate
// the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not
// be as good quality as the game's own (might even be better in some cases though).
// For now, I choose to use autogen mips on GLES2 and the game's own on other platforms.
// As is usual, GLES3 will solve this problem nicely but wide distribution of that is
// years away.
2013-02-10 18:16:11 -08:00
LoadTextureLevel ( * entry , 0 ) ;
2013-02-12 21:00:51 +01:00
if ( maxLevel > 0 )
2013-02-09 20:21:10 +01:00
glGenerateMipmap ( GL_TEXTURE_2D ) ;
# else
2013-02-12 21:00:51 +01:00
for ( int i = 0 ; i < = maxLevel ; i + + ) {
2013-02-10 14:11:53 -08:00
LoadTextureLevel ( * entry , i ) ;
2013-02-08 00:04:34 +01:00
}
2013-02-12 21:00:51 +01:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , maxLevel ) ;
2013-02-09 20:21:10 +01:00
# endif
2013-02-12 21:00:51 +01:00
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LOD , ( float ) maxLevel ) ;
2013-02-11 20:48:07 +08:00
float anisotropyLevel = ( float ) g_Config . iAnisotropyLevel > maxAnisotropyLevel ? maxAnisotropyLevel : ( float ) g_Config . iAnisotropyLevel ;
2013-02-11 19:03:11 +01:00
glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_ANISOTROPY_EXT , anisotropyLevel ) ;
2013-02-11 20:48:07 +08:00
// NOTICE_LOG(G3D,"AnisotropyLevel = %0.1f , MaxAnisotropyLevel = %0.1f ", anisotropyLevel, maxAnisotropyLevel );
2012-11-09 00:51:04 +01:00
2013-02-10 14:11:53 -08:00
UpdateSamplingParams ( * entry , true ) ;
2013-02-08 00:04:34 +01:00
//glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
//glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei ( GL_PACK_ALIGNMENT , 1 ) ;
}
void TextureCache : : LoadTextureLevel ( TexCacheEntry & entry , int level )
{
2012-11-08 23:26:30 +01:00
void * finalBuf = NULL ;
2012-11-01 16:19:01 +01:00
2013-02-08 00:04:34 +01:00
// TODO: only do this once
u32 texByteAlign = 1 ;
2012-11-01 16:19:01 +01:00
// 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.
2013-02-08 00:04:34 +01:00
GLenum dstFmt = 0 ;
2012-11-01 16:19:01 +01:00
2012-11-22 23:07:15 +01:00
// TODO: Actually decode the mipmaps.
2013-02-08 00:04:34 +01:00
u32 texaddr = ( gstate . texaddr [ level ] & 0xFFFFF0 ) | ( ( gstate . texbufwidth [ level ] < < 8 ) & 0x0F000000 ) ;
int bufw = gstate . texbufwidth [ level ] & 0x3ff ;
int w = 1 < < ( gstate . texsize [ level ] & 0xf ) ;
int h = 1 < < ( ( gstate . texsize [ level ] > > 8 ) & 0xf ) ;
const u8 * texptr = Memory : : GetPointer ( texaddr ) ;
switch ( entry . format )
2012-11-01 16:19:01 +01:00
{
2012-11-08 23:26:30 +01:00
case GE_TFMT_CLUT4 :
2013-02-09 19:24:48 +01:00
dstFmt = getClutDestFormat ( ( GEPaletteFormat ) ( entry . clutformat ) ) ;
2012-11-29 15:06:54 +01:00
2013-02-08 00:04:34 +01:00
switch ( entry . clutformat ) {
2012-11-08 23:26:30 +01:00
case GE_CMODE_16BIT_BGR5650 :
case GE_CMODE_16BIT_ABGR5551 :
case GE_CMODE_16BIT_ABGR4444 :
2012-11-01 16:19:01 +01:00
{
2013-01-30 20:40:26 +01:00
ReadClut16 ( clutBuf16 ) ;
const u16 * clut = clutBuf16 ;
2013-02-09 19:24:48 +01:00
u32 clutSharingOffset = 0 ; //(gstate.mipmapShareClut & 1) ? 0 : level * 16;
2012-11-08 23:26:30 +01:00
texByteAlign = 2 ;
2013-01-06 12:11:47 +01:00
if ( ! ( gstate . texmode & 1 ) ) {
2013-02-01 00:02:50 +01:00
const u8 * addr = Memory : : GetPointer ( texaddr ) ;
2012-11-28 16:20:38 +01:00
for ( int i = 0 ; i < bufw * h ; i + = 2 )
2012-11-01 16:19:01 +01:00
{
2013-01-31 00:48:26 -08:00
u8 index = * addr + + ;
2013-02-09 19:24:48 +01:00
tmpTexBuf16 [ i + 0 ] = clut [ GetClutIndex ( ( index > > 0 ) & 0xf ) + clutSharingOffset ] ;
tmpTexBuf16 [ i + 1 ] = clut [ GetClutIndex ( ( index > > 4 ) & 0xf ) + clutSharingOffset ] ;
2012-11-01 16:19:01 +01:00
}
2013-01-06 12:11:47 +01:00
} else {
2012-11-08 23:26:30 +01:00
UnswizzleFromMem ( texaddr , 0 , level ) ;
2012-11-28 16:20:38 +01:00
for ( int i = 0 , j = 0 ; i < bufw * h ; i + = 8 , j + + )
2012-11-01 16:19:01 +01:00
{
2012-11-08 23:26:30 +01:00
u32 n = tmpTexBuf32 [ j ] ;
u32 k , index ;
for ( k = 0 ; k < 8 ; k + + ) {
index = ( n > > ( k * 4 ) ) & 0xf ;
2013-02-09 19:24:48 +01:00
tmpTexBuf16 [ i + k ] = clut [ GetClutIndex ( index ) + clutSharingOffset ] ;
2012-11-08 23:26:30 +01:00
}
2012-11-01 16:19:01 +01:00
}
}
2012-11-08 23:26:30 +01:00
finalBuf = tmpTexBuf16 ;
}
2012-11-01 16:19:01 +01:00
break ;
2012-11-08 23:26:30 +01:00
case GE_CMODE_32BIT_ABGR8888 :
{
2013-01-30 20:40:26 +01:00
ReadClut32 ( clutBuf32 ) ;
const u32 * clut = clutBuf32 ;
2012-11-08 23:26:30 +01:00
u32 clutSharingOff = 0 ; //gstate.mipmapShareClut ? 0 : level * 16;
2013-01-06 12:11:47 +01:00
if ( ! ( gstate . texmode & 1 ) ) {
2013-02-01 00:02:50 +01:00
const u8 * addr = Memory : : GetPointer ( texaddr ) ;
2012-11-28 16:20:38 +01:00
for ( int i = 0 ; i < bufw * h ; i + = 2 )
2012-11-01 16:19:01 +01:00
{
2013-01-31 00:48:26 -08:00
u8 index = * addr + + ;
2012-11-08 23:26:30 +01:00
tmpTexBuf32 [ i + 0 ] = clut [ GetClutIndex ( ( index > > 0 ) & 0xf ) + clutSharingOff ] ;
tmpTexBuf32 [ i + 1 ] = clut [ GetClutIndex ( ( index > > 4 ) & 0xf ) + clutSharingOff ] ;
2012-11-01 16:19:01 +01:00
}
2013-01-06 12:11:47 +01:00
} else {
2012-11-08 23:26:30 +01:00
u32 pixels = bufw * h ;
UnswizzleFromMem ( texaddr , 0 , level ) ;
2013-01-06 12:11:47 +01:00
for ( int i = pixels - 8 , j = ( pixels / 8 ) - 1 ; i > = 0 ; i - = 8 , j - - ) {
2012-11-08 23:26:30 +01:00
u32 n = tmpTexBuf32 [ j ] ;
2012-11-28 16:20:38 +01:00
for ( int k = 0 ; k < 8 ; k + + ) {
u32 index = ( n > > ( k * 4 ) ) & 0xf ;
2012-11-08 23:26:30 +01:00
tmpTexBuf32 [ i + k ] = clut [ GetClutIndex ( index ) + clutSharingOff ] ;
2012-11-01 16:19:01 +01:00
}
}
}
2012-11-08 23:26:30 +01:00
finalBuf = tmpTexBuf32 ;
}
2012-11-01 16:19:01 +01:00
break ;
2012-11-08 23:26:30 +01:00
default :
ERROR_LOG ( G3D , " Unknown CLUT4 texture mode %d " , ( gstate . clutformat & 3 ) ) ;
return ;
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
break ;
2012-11-01 16:19:01 +01:00
case GE_TFMT_CLUT8 :
2012-11-08 23:26:30 +01:00
finalBuf = readIndexedTex ( level , texaddr , 1 ) ;
dstFmt = getClutDestFormat ( ( GEPaletteFormat ) ( gstate . clutformat & 3 ) ) ;
texByteAlign = texByteAlignMap [ ( gstate . clutformat & 3 ) ] ;
break ;
case GE_TFMT_CLUT16 :
finalBuf = readIndexedTex ( level , texaddr , 2 ) ;
dstFmt = getClutDestFormat ( ( GEPaletteFormat ) ( gstate . clutformat & 3 ) ) ;
texByteAlign = texByteAlignMap [ ( gstate . clutformat & 3 ) ] ;
break ;
case GE_TFMT_CLUT32 :
finalBuf = readIndexedTex ( level , texaddr , 4 ) ;
dstFmt = getClutDestFormat ( ( GEPaletteFormat ) ( gstate . clutformat & 3 ) ) ;
texByteAlign = texByteAlignMap [ ( gstate . clutformat & 3 ) ] ;
break ;
case GE_TFMT_4444 :
case GE_TFMT_5551 :
case GE_TFMT_5650 :
2013-02-08 00:04:34 +01:00
if ( entry . format = = GE_TFMT_4444 )
2012-11-09 01:24:19 +01:00
dstFmt = GL_UNSIGNED_SHORT_4_4_4_4 ;
2013-02-08 00:04:34 +01:00
else if ( entry . format = = GE_TFMT_5551 )
2012-11-09 01:24:19 +01:00
dstFmt = GL_UNSIGNED_SHORT_5_5_5_1 ;
2013-02-08 00:04:34 +01:00
else if ( entry . format = = GE_TFMT_5650 )
2012-11-09 01:24:19 +01:00
dstFmt = GL_UNSIGNED_SHORT_5_6_5 ;
2012-11-08 23:26:30 +01:00
texByteAlign = 2 ;
2013-01-06 12:11:47 +01:00
if ( ! ( gstate . texmode & 1 ) ) {
2012-11-28 16:20:38 +01:00
int len = std : : max ( bufw , w ) * h ;
for ( int i = 0 ; i < len ; i + + )
2013-01-06 12:11:47 +01:00
tmpTexBuf16 [ i ] = Memory : : ReadUnchecked_U16 ( texaddr + i * 2 ) ;
2012-11-08 23:26:30 +01:00
finalBuf = tmpTexBuf16 ;
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
else
finalBuf = UnswizzleFromMem ( texaddr , 2 , level ) ;
break ;
2012-11-01 16:19:01 +01:00
case GE_TFMT_8888 :
2012-11-08 23:26:30 +01:00
dstFmt = GL_UNSIGNED_BYTE ;
2013-01-06 12:11:47 +01:00
if ( ! ( gstate . texmode & 1 ) ) {
2012-11-28 16:20:38 +01:00
int len = bufw * h ;
for ( int i = 0 ; i < len ; i + + )
2013-01-06 12:11:47 +01:00
tmpTexBuf32 [ i ] = Memory : : ReadUnchecked_U32 ( texaddr + i * 4 ) ;
2012-11-08 23:26:30 +01:00
finalBuf = tmpTexBuf32 ;
2012-11-01 16:19:01 +01:00
}
2012-11-08 23:26:30 +01:00
else
finalBuf = UnswizzleFromMem ( texaddr , 4 , level ) ;
break ;
2012-11-01 16:19:01 +01:00
case GE_TFMT_DXT1 :
2012-11-08 23:26:30 +01:00
dstFmt = GL_UNSIGNED_BYTE ;
2012-11-01 16:19:01 +01:00
{
2012-11-08 23:26:30 +01:00
u32 * dst = tmpTexBuf32 ;
2012-11-01 16:19:01 +01:00
DXT1Block * src = ( DXT1Block * ) texptr ;
2013-01-06 12:11:47 +01:00
for ( int y = 0 ; y < h ; y + = 4 ) {
2012-11-28 16:12:29 +01:00
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
2013-01-06 12:11:47 +01:00
for ( int x = 0 ; x < std : : min ( bufw , w ) ; x + = 4 ) {
2012-11-28 16:12:29 +01:00
decodeDXT1Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
blockIndex + + ;
2012-11-01 16:19:01 +01:00
}
}
2012-11-22 23:07:15 +01:00
finalBuf = tmpTexBuf32 ;
2012-11-28 16:12:29 +01:00
w = ( w + 3 ) & ~ 3 ;
2012-11-01 16:19:01 +01:00
}
break ;
case GE_TFMT_DXT3 :
2012-11-28 16:12:29 +01:00
dstFmt = GL_UNSIGNED_BYTE ;
{
u32 * dst = tmpTexBuf32 ;
DXT3Block * src = ( DXT3Block * ) texptr ;
2012-11-29 15:06:54 +01:00
// Alpha is off
2013-01-06 12:11:47 +01:00
for ( int y = 0 ; y < h ; y + = 4 ) {
2012-11-28 16:20:38 +01:00
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
2013-01-06 12:11:47 +01:00
for ( int x = 0 ; x < std : : min ( bufw , w ) ; x + = 4 ) {
2012-11-28 16:12:29 +01:00
decodeDXT3Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
blockIndex + + ;
}
}
w = ( w + 3 ) & ~ 3 ;
finalBuf = tmpTexBuf32 ;
}
break ;
2012-11-01 16:19:01 +01:00
case GE_TFMT_DXT5 :
2013-02-08 00:04:34 +01:00
ERROR_LOG ( G3D , " Unhandled compressed texture, format %i! swizzle=%i " , entry . format , gstate . texmode & 1 ) ;
2012-11-28 16:12:29 +01:00
dstFmt = GL_UNSIGNED_BYTE ;
{
u32 * dst = tmpTexBuf32 ;
DXT5Block * src = ( DXT5Block * ) texptr ;
2012-11-29 15:06:54 +01:00
// Alpha is almost right
2013-01-06 12:11:47 +01:00
for ( int y = 0 ; y < h ; y + = 4 ) {
2012-11-28 16:12:29 +01:00
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
2013-01-06 12:11:47 +01:00
for ( int x = 0 ; x < std : : min ( bufw , w ) ; x + = 4 ) {
2012-11-28 16:12:29 +01:00
decodeDXT5Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
blockIndex + + ;
}
}
w = ( w + 3 ) & ~ 3 ;
finalBuf = tmpTexBuf32 ;
}
2012-11-08 23:26:30 +01:00
break ;
2012-11-01 16:19:01 +01:00
default :
2013-02-08 00:04:34 +01:00
ERROR_LOG ( G3D , " Unknown Texture Format %d!!! " , entry . format ) ;
2012-11-28 16:12:29 +01:00
finalBuf = tmpTexBuf32 ;
2012-11-01 16:19:01 +01:00
return ;
}
2012-11-28 16:12:29 +01:00
if ( ! finalBuf ) {
ERROR_LOG ( G3D , " NO finalbuf! Will crash! " ) ;
}
2012-11-09 01:24:19 +01:00
convertColors ( ( u8 * ) finalBuf , dstFmt , bufw * h ) ;
2012-11-09 00:51:04 +01:00
if ( w ! = bufw ) {
int pixelSize ;
switch ( dstFmt ) {
2012-11-09 01:24:19 +01:00
case GL_UNSIGNED_SHORT_4_4_4_4 :
case GL_UNSIGNED_SHORT_5_5_5_1 :
case GL_UNSIGNED_SHORT_5_6_5 :
2012-11-09 00:51:04 +01:00
pixelSize = 2 ;
2012-11-09 01:24:19 +01:00
break ;
2012-11-09 00:51:04 +01:00
default :
pixelSize = 4 ;
2012-11-09 01:24:19 +01:00
break ;
2012-11-09 00:51:04 +01:00
}
// Need to rearrange the buffer to simulate GL_UNPACK_ROW_LENGTH etc.
int inRowBytes = bufw * pixelSize ;
int outRowBytes = w * pixelSize ;
const u8 * read = ( const u8 * ) finalBuf ;
u8 * write = 0 ;
if ( w > bufw ) {
write = ( u8 * ) tmpTexBufRearrange ;
finalBuf = tmpTexBufRearrange ;
} else {
write = ( u8 * ) finalBuf ;
}
2012-11-28 16:20:38 +01:00
for ( int y = 0 ; y < h ; y + + ) {
2012-11-09 00:51:04 +01:00
memmove ( write , read , outRowBytes ) ;
read + = inRowBytes ;
write + = outRowBytes ;
}
}
2012-12-21 21:49:09 +01:00
gpuStats . numTexturesDecoded + + ;
2012-11-09 00:51:04 +01:00
// Can restore these and remove the above fixup on some platforms.
//glPixelStorei(GL_UNPACK_ROW_LENGTH, bufw);
2012-12-22 00:38:17 +01:00
glPixelStorei ( GL_UNPACK_ALIGNMENT , texByteAlign ) ;
2012-11-09 00:51:04 +01:00
//glPixelStorei(GL_PACK_ROW_LENGTH, bufw);
2012-12-22 00:38:17 +01:00
glPixelStorei ( GL_PACK_ALIGNMENT , texByteAlign ) ;
2012-12-21 21:49:09 +01:00
2013-02-09 21:32:02 +01:00
// INFO_LOG(G3D, "Creating texture level %i/%i from %08x: %i x %i (stride: %i). fmt: %i", level, entry.maxLevel, texaddr, w, h, bufw, entry.format);
2012-11-08 23:26:30 +01:00
2012-11-26 01:21:14 +01:00
GLuint components = dstFmt = = GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA ;
2013-02-08 00:04:34 +01:00
glTexImage2D ( GL_TEXTURE_2D , level , components , w , h , 0 , components , dstFmt , finalBuf ) ;
2012-11-01 16:19:01 +01:00
}
2013-02-17 01:06:06 +01:00
bool TextureCache : : DecodeTexture ( u8 * output , GPUgstate state )
{
GPUgstate oldState = gstate ;
gstate = state ;
u32 texaddr = ( gstate . texaddr [ 0 ] & 0xFFFFF0 ) | ( ( gstate . texbufwidth [ 0 ] < < 8 ) & 0x0F000000 ) ;
if ( ! Memory : : IsValidAddress ( texaddr ) ) {
return false ;
}
u8 level = 0 ;
u32 format = gstate . texformat & 0xF ;
if ( format > = 11 ) {
ERROR_LOG ( G3D , " Unknown texture format %i " , format ) ;
format = 0 ;
}
u32 clutformat = gstate . clutformat & 3 ;
const u8 * texptr = Memory : : GetPointer ( texaddr ) ;
int bufw = gstate . texbufwidth [ 0 ] & 0x3ff ;
int w = 1 < < ( gstate . texsize [ 0 ] & 0xf ) ;
int h = 1 < < ( ( gstate . texsize [ 0 ] > > 8 ) & 0xf ) ;
GLenum dstFmt = 0 ;
u32 texByteAlign = 1 ;
void * finalBuf = NULL ;
// 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_CLUT4 :
dstFmt = getClutDestFormat ( ( GEPaletteFormat ) ( gstate . clutformat & 3 ) ) ;
switch ( clutformat ) {
case GE_CMODE_16BIT_BGR5650 :
case GE_CMODE_16BIT_ABGR5551 :
case GE_CMODE_16BIT_ABGR4444 :
{
ReadClut16 ( clutBuf16 ) ;
const u16 * clut = clutBuf16 ;
u32 clutSharingOff = 0 ; //gstate.mipmapShareClut ? 0 : level * 16;
texByteAlign = 2 ;
if ( ! ( gstate . texmode & 1 ) ) {
const u8 * addr = Memory : : GetPointer ( texaddr ) ;
for ( int i = 0 ; i < bufw * h ; i + = 2 )
{
u8 index = * addr + + ;
tmpTexBuf16 [ i + 0 ] = clut [ GetClutIndex ( ( index > > 0 ) & 0xf ) + clutSharingOff ] ;
tmpTexBuf16 [ i + 1 ] = clut [ GetClutIndex ( ( index > > 4 ) & 0xf ) + clutSharingOff ] ;
}
} else {
UnswizzleFromMem ( texaddr , 0 , level ) ;
for ( int i = 0 , j = 0 ; i < bufw * h ; i + = 8 , j + + )
{
u32 n = tmpTexBuf32 [ j ] ;
u32 k , index ;
for ( k = 0 ; k < 8 ; k + + ) {
index = ( n > > ( k * 4 ) ) & 0xf ;
tmpTexBuf16 [ i + k ] = clut [ GetClutIndex ( index ) + clutSharingOff ] ;
}
}
}
finalBuf = tmpTexBuf16 ;
}
break ;
case GE_CMODE_32BIT_ABGR8888 :
{
ReadClut32 ( clutBuf32 ) ;
const u32 * clut = clutBuf32 ;
u32 clutSharingOff = 0 ; //gstate.mipmapShareClut ? 0 : level * 16;
if ( ! ( gstate . texmode & 1 ) ) {
const u8 * addr = Memory : : GetPointer ( texaddr ) ;
for ( int i = 0 ; i < bufw * h ; i + = 2 )
{
u8 index = * addr + + ;
tmpTexBuf32 [ i + 0 ] = clut [ GetClutIndex ( ( index > > 0 ) & 0xf ) + clutSharingOff ] ;
tmpTexBuf32 [ i + 1 ] = clut [ GetClutIndex ( ( index > > 4 ) & 0xf ) + clutSharingOff ] ;
}
} else {
u32 pixels = bufw * h ;
UnswizzleFromMem ( texaddr , 0 , level ) ;
for ( int i = pixels - 8 , j = ( pixels / 8 ) - 1 ; i > = 0 ; i - = 8 , j - - ) {
u32 n = tmpTexBuf32 [ j ] ;
for ( int k = 0 ; k < 8 ; k + + ) {
u32 index = ( n > > ( k * 4 ) ) & 0xf ;
tmpTexBuf32 [ i + k ] = clut [ GetClutIndex ( index ) + clutSharingOff ] ;
}
}
}
finalBuf = tmpTexBuf32 ;
}
break ;
default :
ERROR_LOG ( G3D , " Unknown CLUT4 texture mode %d " , ( gstate . clutformat & 3 ) ) ;
return false ;
}
break ;
case GE_TFMT_CLUT8 :
finalBuf = readIndexedTex ( level , texaddr , 1 ) ;
dstFmt = getClutDestFormat ( ( GEPaletteFormat ) ( gstate . clutformat & 3 ) ) ;
texByteAlign = texByteAlignMap [ ( gstate . clutformat & 3 ) ] ;
break ;
case GE_TFMT_CLUT16 :
finalBuf = readIndexedTex ( level , texaddr , 2 ) ;
dstFmt = getClutDestFormat ( ( GEPaletteFormat ) ( gstate . clutformat & 3 ) ) ;
texByteAlign = texByteAlignMap [ ( gstate . clutformat & 3 ) ] ;
break ;
case GE_TFMT_CLUT32 :
finalBuf = readIndexedTex ( level , texaddr , 4 ) ;
dstFmt = getClutDestFormat ( ( GEPaletteFormat ) ( gstate . clutformat & 3 ) ) ;
texByteAlign = texByteAlignMap [ ( gstate . clutformat & 3 ) ] ;
break ;
case GE_TFMT_4444 :
case GE_TFMT_5551 :
case GE_TFMT_5650 :
if ( format = = GE_TFMT_4444 )
dstFmt = GL_UNSIGNED_SHORT_4_4_4_4 ;
else if ( format = = GE_TFMT_5551 )
dstFmt = GL_UNSIGNED_SHORT_5_5_5_1 ;
else if ( format = = GE_TFMT_5650 )
dstFmt = GL_UNSIGNED_SHORT_5_6_5 ;
texByteAlign = 2 ;
if ( ! ( gstate . texmode & 1 ) ) {
int len = std : : max ( bufw , w ) * h ;
for ( int i = 0 ; i < len ; i + + )
tmpTexBuf16 [ i ] = Memory : : ReadUnchecked_U16 ( texaddr + i * 2 ) ;
finalBuf = tmpTexBuf16 ;
}
else
finalBuf = UnswizzleFromMem ( texaddr , 2 , level ) ;
break ;
case GE_TFMT_8888 :
dstFmt = GL_UNSIGNED_BYTE ;
if ( ! ( gstate . texmode & 1 ) ) {
int len = bufw * h ;
for ( int i = 0 ; i < len ; i + + )
tmpTexBuf32 [ i ] = Memory : : ReadUnchecked_U32 ( texaddr + i * 4 ) ;
finalBuf = tmpTexBuf32 ;
}
else
finalBuf = UnswizzleFromMem ( texaddr , 4 , level ) ;
break ;
case GE_TFMT_DXT1 :
dstFmt = GL_UNSIGNED_BYTE ;
{
u32 * dst = tmpTexBuf32 ;
DXT1Block * src = ( DXT1Block * ) texptr ;
for ( int y = 0 ; y < h ; y + = 4 ) {
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
for ( int x = 0 ; x < std : : min ( bufw , w ) ; x + = 4 ) {
decodeDXT1Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
blockIndex + + ;
}
}
finalBuf = tmpTexBuf32 ;
w = ( w + 3 ) & ~ 3 ;
}
break ;
case GE_TFMT_DXT3 :
dstFmt = GL_UNSIGNED_BYTE ;
{
u32 * dst = tmpTexBuf32 ;
DXT3Block * src = ( DXT3Block * ) texptr ;
// Alpha is off
for ( int y = 0 ; y < h ; y + = 4 ) {
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
for ( int x = 0 ; x < std : : min ( bufw , w ) ; x + = 4 ) {
decodeDXT3Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
blockIndex + + ;
}
}
w = ( w + 3 ) & ~ 3 ;
finalBuf = tmpTexBuf32 ;
}
break ;
case GE_TFMT_DXT5 :
ERROR_LOG ( G3D , " Unhandled compressed texture, format %i! swizzle=%i " , format , gstate . texmode & 1 ) ;
dstFmt = GL_UNSIGNED_BYTE ;
{
u32 * dst = tmpTexBuf32 ;
DXT5Block * src = ( DXT5Block * ) texptr ;
// Alpha is almost right
for ( int y = 0 ; y < h ; y + = 4 ) {
u32 blockIndex = ( y / 4 ) * ( bufw / 4 ) ;
for ( int x = 0 ; x < std : : min ( bufw , w ) ; x + = 4 ) {
decodeDXT5Block ( dst + bufw * y + x , src + blockIndex , bufw ) ;
blockIndex + + ;
}
}
w = ( w + 3 ) & ~ 3 ;
finalBuf = tmpTexBuf32 ;
}
break ;
default :
ERROR_LOG ( G3D , " Unknown Texture Format %d!!! " , format ) ;
finalBuf = tmpTexBuf32 ;
return false ;
}
if ( ! finalBuf ) {
ERROR_LOG ( G3D , " NO finalbuf! Will crash! " ) ;
}
convertColors ( ( u8 * ) finalBuf , dstFmt , bufw * h ) ;
if ( dstFmt = = GL_UNSIGNED_SHORT_4_4_4_4 )
{
for ( int x = 0 ; x < h ; x + + )
for ( int y = 0 ; y < bufw ; y + + )
{
u32 val = ( ( u16 * ) finalBuf ) [ x * bufw + y ] ;
u32 a = ( val & 0xF ) * 255 / 15 ;
u32 r = ( ( val & 0xF ) > > 24 ) * 255 / 15 ;
u32 g = ( ( val & 0xF ) > > 16 ) * 255 / 15 ;
u32 b = ( ( val & 0xF ) > > 8 ) * 255 / 15 ;
( ( u32 * ) output ) [ x * w + y ] = ( a < < 24 ) | ( r < < 16 ) | ( g < < 8 ) | b ;
}
}
else if ( dstFmt = = GL_UNSIGNED_SHORT_5_5_5_1 )
{
for ( int x = 0 ; x < h ; x + + )
for ( int y = 0 ; y < bufw ; y + + )
{
u32 val = ( ( u16 * ) finalBuf ) [ x * bufw + y ] ;
u32 a = ( val & 0x1 ) * 255 ;
u32 r = ( ( val & 0x1F ) > > 11 ) * 255 / 31 ;
u32 g = ( ( val & 0x1F ) > > 6 ) * 255 / 31 ;
u32 b = ( ( val & 0x1F ) > > 1 ) * 255 / 31 ;
( ( u32 * ) output ) [ x * w + y ] = ( a < < 24 ) | ( r < < 16 ) | ( g < < 8 ) | b ;
}
}
else if ( dstFmt = = GL_UNSIGNED_SHORT_5_6_5 )
{
for ( int x = 0 ; x < h ; x + + )
for ( int y = 0 ; y < bufw ; y + + )
{
u32 val = ( ( u16 * ) finalBuf ) [ x * bufw + y ] ;
u32 a = 0xFF ;
u32 r = ( ( val & 0x1F ) > > 11 ) * 255 / 31 ;
u32 g = ( ( val & 0x3F ) > > 6 ) * 255 / 63 ;
u32 b = ( ( val & 0x1F ) ) * 255 / 31 ;
( ( u32 * ) output ) [ x * w + y ] = ( a < < 24 ) | ( r < < 16 ) | ( g < < 8 ) | b ;
}
}
else
{
for ( int x = 0 ; x < h ; x + + )
for ( int y = 0 ; y < bufw ; y + + )
{
u32 val = ( ( u32 * ) finalBuf ) [ x * bufw + y ] ;
( ( u32 * ) output ) [ x * w + y ] = ( ( val & 0xFF000000 ) ) | ( ( val & 0x00FF0000 ) > > 16 ) | ( ( val & 0x0000FF00 ) ) | ( ( val & 0x000000FF ) < < 16 ) ;
}
}
gstate = oldState ;
return true ;
}