rewrite texture cache, change commandline from --single-core to --num-cores=N, add multithreading to rasterizer, add toggles to 3d config to disable fog+edgemarking for little speedups in games that use them.

This commit is contained in:
zeromus 2009-10-28 09:39:52 +00:00
parent dd117dc47f
commit 8934925019
21 changed files with 1749 additions and 1192 deletions

View File

@ -55,9 +55,6 @@ GPU::MosaicLookup GPU::mosaicLookup;
//#define DEBUG_TRI
CACHE_ALIGN u8 GPU_screen[4*256*192];
u8 *GPU_tempScanline;
CACHE_ALIGN u16 GPU_tempScanlineBuffer[256];
CACHE_ALIGN u8 sprWin[256];
@ -2237,7 +2234,7 @@ template<bool SKIP> static void GPU_RenderLine_DispCapture(u16 l)
//INFO("Capture screen (BG + OBJ + 3D)\n");
u8 *src;
src = (u8*)(GPU_tempScanline);
src = (u8*)(gpu->tempScanline);
CAPCOPY(src,cap_dst);
}
break;
@ -2279,7 +2276,7 @@ template<bool SKIP> static void GPU_RenderLine_DispCapture(u16 l)
if (gpu->dispCapCnt.srcA == 0)
{
// Capture screen (BG + OBJ + 3D)
srcA = (u16*)(GPU_tempScanline);
srcA = (u16*)(gpu->tempScanline);
}
else
{
@ -2579,10 +2576,10 @@ void GPU_RenderLine(NDS_Screen * screen, u16 l, bool skip)
//generate the 2d engine output
if(gpu->dispMode == 1) {
//optimization: render straight to the output buffer when thats what we are going to end up displaying anyway
GPU_tempScanline = screen->gpu->currDst = (u8 *)(GPU_screen) + (screen->offset + l) * 512;
gpu->tempScanline = screen->gpu->currDst = (u8 *)(GPU_screen) + (screen->offset + l) * 512;
} else {
//otherwise, we need to go to a temp buffer
GPU_tempScanline = screen->gpu->currDst = (u8 *)GPU_tempScanlineBuffer;
gpu->tempScanline = screen->gpu->currDst = (u8 *)gpu->tempScanlineBuffer;
}
GPU_RenderLine_layer(screen, l);

View File

@ -736,6 +736,9 @@ struct GPU
u16 *currentFadeInColors, *currentFadeOutColors;
bool blend2[8];
CACHE_ALIGN u16 tempScanlineBuffer[256];
u8 *tempScanline;
u8 MasterBrightMode;
u32 MasterBrightFactor;

View File

@ -43,7 +43,8 @@ libdesmume_a_SOURCES = \
utils/md5.cpp utils/md5.h utils/valuearray.h utils/xstring.cpp utils/xstring.h \
utils/decrypt/crc.cpp utils/decrypt/crc.h utils/decrypt/decrypt.cpp \
utils/decrypt/decrypt.h utils/decrypt/header.cpp utils/decrypt/header.h \
addons.cpp addons.h \
utils/task.cpp utils/task.h \
addons.cpp addons.h \
addons/compactFlash.cpp addons/gbagame.cpp addons/none.cpp addons/rumblepak.cpp addons/guitarGrip.cpp addons/expMemory.cpp fs.h \
cheatSystem.cpp cheatSystem.h \
texcache.cpp texcache.h rasterize.cpp rasterize.h \

View File

@ -1881,6 +1881,14 @@ void Sequencer::init()
#endif
}
//this isnt helping much right now. work on it later
//#include "utils/task.h"
//Task taskSubGpu(true);
//void* renderSubScreen(void*)
//{
// GPU_RenderLine(&SubScreen, nds.VCount, SkipCur2DFrame);
// return NULL;
//}
static void execHardware_hblank()
{
@ -1907,8 +1915,10 @@ static void execHardware_hblank()
//in practice we need to be more forgiving, in case things have overrun the scanline start.
//this should be safe since games cannot do anything timing dependent until this next
//scanline begins, anyway (as this scanline was in the middle of drawing)
//taskSubGpu.execute(renderSubScreen,NULL);
GPU_RenderLine(&MainScreen, nds.VCount, SkipCur2DFrame);
GPU_RenderLine(&SubScreen, nds.VCount, SkipCur2DFrame);
//taskSubGpu.finish();
//trigger hblank dmas
//but notice, we do that just after we finished drawing the line
@ -1963,12 +1973,12 @@ static void execHardware_hstart_vblankStart()
static void execHardware_hstart_vcount()
{
u16 vmatch = T1ReadWord(MMU.ARM9_REG, 4);
if(nds.VCount==((vmatch>>8)|((vmatch<<1)&(1<<8))))
vmatch = ((vmatch>>8)|((vmatch<<1)&(1<<8)));
if(nds.VCount==vmatch)
{
//arm9 vmatch
T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) | 4);
if(T1ReadWord(MMU.ARM9_REG, 4) & 32) {
//printf("VMATCH FIRING! vc=%03d\n",nds.VCount);
NDS_makeARM9Int(2);
}
}
@ -1976,7 +1986,8 @@ static void execHardware_hstart_vcount()
T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) & 0xFFFB);
vmatch = T1ReadWord(MMU.ARM7_REG, 4);
if(nds.VCount==((vmatch>>8)|((vmatch<<1)&(1<<8))))
vmatch = ((vmatch>>8)|((vmatch<<1)&(1<<8)));
if(nds.VCount==vmatch)
{
//arm7 vmatch
T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 4);

View File

@ -421,17 +421,19 @@ int NDS_WriteBMP_32bppBuffer(int width, int height, const void* buf, const char
extern struct TCommonSettings {
TCommonSettings()
: HighResolutionInterpolateColor(true)
, UseExtBIOS(false)
: UseExtBIOS(false)
, SWIFromBIOS(false)
, UseExtFirmware(false)
, BootFromFirmware(false)
, DebugConsole(false)
, single_core(true)
, num_cores(1)
, spuInterpolationMode(SPUInterpolation_Linear)
//, gfx3d_flushMode(0)
, manualBackupType(0)
, micMode(InternalNoise)
, GFX3D_HighResolutionInterpolateColor(true)
, GFX3D_EdgeMark(true)
, GFX3D_Fog(true)
{
strcpy(ARM9BIOS, "biosnds9.bin");
strcpy(ARM7BIOS, "biosnds7.bin");
@ -443,7 +445,9 @@ extern struct TCommonSettings {
for(int i=0;i<16;i++)
spu_muteChannels[i] = false;
}
bool HighResolutionInterpolateColor;
bool GFX3D_HighResolutionInterpolateColor;
bool GFX3D_EdgeMark;
bool GFX3D_Fog;
bool UseExtBIOS;
char ARM9BIOS[256];
@ -456,7 +460,8 @@ extern struct TCommonSettings {
bool DebugConsole;
bool single_core;
int num_cores;
bool single_core() { return num_cores==1; }
struct _Wifi {
int mode;

View File

@ -24,6 +24,8 @@
//so, it doesnt composite to 2d correctly.
//(re: new super mario brothers renders the stormclouds at the beginning)
#include <queue>
#include "OGLRender.h"
#include "debug.h"
@ -208,9 +210,8 @@ static void _xglDisable(GLenum cap) {
CTASSERT((cap-0x0B00)<0x100); \
_xglDisable(cap); }
static std::queue<GLuint> freeTextureIds;
GLenum oglTempTextureID[MAX_TEXTURE];
GLenum oglToonTableTextureID;
#define NOSHADERS(s) { hasShaders = false; INFO("Shaders aren't supported on your system, using fixed pipeline\n(%s)\n", s); return; }
@ -252,17 +253,16 @@ GLenum oglToonTableTextureID;
bool hasShaders = false;
/* Vertex shader */
GLuint vertexShaderID;
/* Fragment shader */
GLuint fragmentShaderID;
/* Shader program */
GLuint shaderProgram;
static GLuint hasTexLoc;
static GLuint texBlendLoc;
static bool hasTexture = false;
static ADPCMCacheItem* currTexture = NULL;
/* Shaders init */
static void createShaders()
@ -337,45 +337,54 @@ static void OGLReset()
}
TexCache_Reset();
for (int i = 0; i < MAX_TEXTURE; i++)
texcache[i].id=oglTempTextureID[i];
currTexture = NULL;
// memset(GPU_screenStencil,0,sizeof(GPU_screenStencil));
memset(GPU_screen3D,0,sizeof(GPU_screen3D));
}
//static class OGLTexCacheUser : public ITexCacheUser
//{
//public:
// virtual void BindTexture(u32 tx)
// {
// glBindTexture(GL_TEXTURE_2D,(GLuint)texcache[tx].id);
// glMatrixMode (GL_TEXTURE);
// glLoadIdentity ();
// glScaled (texcache[tx].invSizeX, texcache[tx].invSizeY, 1.0f);
//
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (BIT16(texcache[tx].frm) ? (BIT18(texcache[tx].frm)?GL_MIRRORED_REPEAT:GL_REPEAT) : GL_CLAMP));
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (BIT17(texcache[tx].frm) ? (BIT19(texcache[tx].frm)?GL_MIRRORED_REPEAT:GL_REPEAT) : GL_CLAMP));
// }
//
// virtual void BindTextureData(u32 tx, u8* data)
// {
// BindTexture(tx);
//
// #if 0
// for (int i=0; i < texcache[tx].sizeX * texcache[tx].sizeY*4; i++)
// data[i] = 0xFF;
// #endif
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
// texcache[tx].sizeX, texcache[tx].sizeY, 0,
// GL_RGBA, GL_UNSIGNED_BYTE, data);
// }
//} textures;
//
//static TexCacheUnit texCacheUnit;
static void BindTexture(u32 tx)
static void expandFreeTextures()
{
glBindTexture(GL_TEXTURE_2D,(GLuint)texcache[tx].id);
glMatrixMode (GL_TEXTURE);
glLoadIdentity ();
glScaled (texcache[tx].invSizeX, texcache[tx].invSizeY, 1.0f);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (BIT16(texcache[tx].frm) ? (BIT18(texcache[tx].frm)?GL_MIRRORED_REPEAT:GL_REPEAT) : GL_CLAMP));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (BIT17(texcache[tx].frm) ? (BIT19(texcache[tx].frm)?GL_MIRRORED_REPEAT:GL_REPEAT) : GL_CLAMP));
const int kInitTextures = 128;
GLuint oglTempTextureID[kInitTextures];
glGenTextures(kInitTextures, &oglTempTextureID[0]);
for(int i=0;i<kInitTextures;i++)
freeTextureIds.push(oglTempTextureID[i]);
}
static void BindTextureData(u32 tx, u8* data)
{
BindTexture(tx);
#if 0
for (int i=0; i < texcache[tx].sizeX * texcache[tx].sizeY*4; i++)
data[i] = 0xFF;
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
texcache[tx].sizeX, texcache[tx].sizeY, 0,
GL_RGBA, GL_UNSIGNED_BYTE, data);
}
static char OGLInit(void)
{
GLuint loc = 0;
@ -388,9 +397,7 @@ static char OGLInit(void)
if(!BEGINGL())
return 0;
TexCache_BindTexture = BindTexture;
TexCache_BindTextureData = BindTextureData;
glGenTextures (MAX_TEXTURE, &oglTempTextureID[0]);
expandFreeTextures();
glPixelStorei(GL_PACK_ALIGNMENT,8);
@ -498,12 +505,28 @@ static void OGLClose()
hasShaders = false;
}
glDeleteTextures(MAX_TEXTURE, &oglTempTextureID[0]);
//kill the tex cache to free all the texture ids
TexCache_Reset();
while(!freeTextureIds.empty())
{
GLuint temp = freeTextureIds.front();
freeTextureIds.pop();
glDeleteTextures(1,&temp);
}
//glDeleteTextures(MAX_TEXTURE, &oglTempTextureID[0]);
glDeleteTextures(1, &oglToonTableTextureID);
ENDGL();
}
static void texDeleteCallback(ADPCMCacheItem* item)
{
freeTextureIds.push((GLuint)item->texid);
if(currTexture == item)
currTexture = NULL;
}
static void setTexture(unsigned int format, unsigned int texpal)
{
textureFormat = format;
@ -529,7 +552,43 @@ static void setTexture(unsigned int format, unsigned int texpal)
}
TexCache_SetTexture<TexFormat_32bpp>(format, texpal);
// texCacheUnit.TexCache_SetTexture<TexFormat_32bpp>(format, texpal);
ADPCMCacheItem* newTexture = TexCache_SetTexture(TexFormat_32bpp,format,texpal);
if(newTexture != currTexture)
{
currTexture = newTexture;
//has the ogl renderer initialized the texture?
if(!currTexture->deleteCallback)
{
currTexture->deleteCallback = texDeleteCallback;
if(freeTextureIds.empty()) expandFreeTextures();
currTexture->texid = (void*)freeTextureIds.front();
freeTextureIds.pop();
glBindTexture(GL_TEXTURE_2D,(GLuint)currTexture->texid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (BIT16(currTexture->texformat) ? (BIT18(currTexture->texformat)?GL_MIRRORED_REPEAT:GL_REPEAT) : GL_CLAMP));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (BIT17(currTexture->texformat) ? (BIT19(currTexture->texformat)?GL_MIRRORED_REPEAT:GL_REPEAT) : GL_CLAMP));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
currTexture->sizeX, currTexture->sizeY, 0,
GL_RGBA, GL_UNSIGNED_BYTE, currTexture->decoded);
}
else
{
//otherwise, just bind it
glBindTexture(GL_TEXTURE_2D,(GLuint)currTexture->texid);
}
//in either case, we need to setup the tex mtx
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(currTexture->invSizeX, currTexture->invSizeY, 1.0f);
}
}
@ -902,6 +961,9 @@ static void OGLRender()
}
}
//needs to happen before endgl because it could free some textureids for expired cache items
TexCache_EvictFrame();
ENDGL();
GL_ReadFramebuffer();

View File

@ -148,7 +148,7 @@ void Agg_init()
aggDraw.target = targets[0];
//if we're single core, we don't want to waste time compositing
if(CommonSettings.single_core)
if(CommonSettings.single_core())
aggDraw.hud = &agg_targetScreen;
//and the more clever compositing isnt supported in non-windows

View File

@ -40,8 +40,7 @@ CommandLine::CommandLine()
, _record_movie_file(0)
, _cflash_image(0)
, _cflash_path(0)
, _single_core(0)
, _multi_core(0)
, _num_cores(-1)
, _bios_arm9(NULL)
, _bios_arm7(NULL)
, _bios_swi(0)
@ -74,8 +73,7 @@ void CommandLine::loadCommonOptions()
{ "bios-arm7", 0, 0, G_OPTION_ARG_FILENAME, &_bios_arm7, "Uses the arm7 bios provided at the specified path", "BIOS_ARM7_PATH"},
{ "bios-swi", 0, 0, G_OPTION_ARG_INT, &_bios_swi, "Uses SWI from the provided bios files", "BIOS_SWI"},
#ifdef _MSC_VER
{ "single-core", 0, 0, G_OPTION_ARG_NONE, &_single_core, "Limit execution to use approximately only one core", "NUM_CORES"},
{ "multi-core", 0, 0, G_OPTION_ARG_NONE, &_multi_core, "Act as if multiple cores are present, even on a single-core machine", "MULTI_CORE"},
{ "num-cores", 0, 0, G_OPTION_ARG_NONE, &_num_cores, "Override numcores detection and use this many", "NUM_CORES"},
{ "scanline-filter-a", 0, 0, G_OPTION_ARG_INT, &scanline_filter_a, "Intensity of fadeout for scanlines filter (edge) (default 2)", "SCANLINE_FILTER_A"},
{ "scanline-filter-b", 0, 0, G_OPTION_ARG_INT, &scanline_filter_b, "Intensity of fadeout for scanlines filter (corner) (default 4)", "SCANLINE_FILTER_B"},
#endif
@ -103,8 +101,7 @@ bool CommandLine::parse(int argc,char **argv)
if(_cflash_image) cflash_image = _cflash_image;
if(_cflash_path) cflash_path = _cflash_path;
if(_single_core) CommonSettings.single_core = true;
if(_multi_core) CommonSettings.single_core = false;
if(_num_cores != -1) CommonSettings.num_cores = _num_cores;
//TODO MAX PRIORITY! change ARM9BIOS etc to be a std::string
if(_bios_arm9) { CommonSettings.UseExtBIOS = true; strcpy(CommonSettings.ARM9BIOS,_bios_arm9); }

View File

@ -75,8 +75,7 @@ private:
char* _cflash_path;
char* _bios_arm9, *_bios_arm7;
int _bios_swi;
int _single_core;
int _multi_core;
int _num_cores;
};
#endif

View File

@ -2341,7 +2341,7 @@ static FORCEINLINE VERT clipPoint(VERT* inside, VERT* outside, int coord, int wh
INTERP(coord[0]); INTERP(coord[1]); INTERP(coord[2]); INTERP(coord[3]);
INTERP(texcoord[0]); INTERP(texcoord[1]);
if(CommonSettings.HighResolutionInterpolateColor)
if(CommonSettings.GFX3D_HighResolutionInterpolateColor)
{
INTERP(fcolor[0]); INTERP(fcolor[1]); INTERP(fcolor[2]);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,4 @@
/* Copyright (C) 2006 yopyop
yopyop156@ifrance.com
yopyop156.ifrance.com
Copyright 2009 DeSmuME team
/* Copyright 2009 DeSmuME team
This file is part of DeSmuME

View File

@ -1,7 +1,8 @@
#include "texcache.h"
#include <string.h>
#include <algorithm>
#include <assert.h>
#include "texcache.h"
#include "bits.h"
#include "common.h"
@ -15,6 +16,8 @@ using std::max;
//only dump this from ogl renderer. for now, softrasterizer creates things in an incompatible pixel format
//#define DEBUG_DUMP_TEXTURE
#define CONVERT(color,alpha) ((TEXFORMAT == TexFormat_32bpp)?(RGB15TO32(color,alpha)):RGB15TO6665(color,alpha))
//This class represents a number of regions of memory which should be viewed as contiguous
class MemSpan
{
@ -54,6 +57,8 @@ public:
return 0;
}
//TODO - get rid of duplication between these two methods.
//dumps the memspan to the specified buffer
//you may set size to limit the size to be copied
int dump(void* buf, int size=-1)
@ -160,12 +165,6 @@ static MemSpan MemSpan_TexPalette(u32 ofs, u32 len)
return ret;
}
TextureCache *texcache;
u32 texcache_start;
u32 texcache_stop;
u8 *TexCache_texMAP = NULL;
#if defined (DEBUG_DUMP_TEXTURE) && defined (WIN32)
#define DO_DEBUG_DUMP_TEXTURE
static void DebugDumpTexture(int which)
@ -178,476 +177,527 @@ static void DebugDumpTexture(int which)
#endif
static int lastTexture = -1;
#define CONVERT(color,alpha) ((TEXFORMAT == TexFormat_32bpp)?(RGB15TO32(color,alpha)):RGB15TO6665(color,alpha))
template<TexCache_TexFormat TEXFORMAT>
void TexCache_SetTexture(u32 format, u32 texpal)
//notes on the cache:
//I am really unhappy with the ref counting. this needs to be automatic.
//We could do something better than a linear search through cache items, but it may not be worth it.
//Also we may need to rescan more often (every time a sample loops)
class ADPCMCache
{
//for each texformat, number of palette entries
const int palSizes[] = {0, 32, 4, 16, 256, 0, 8, 0};
public:
ADPCMCache()
: list_front(NULL)
, list_back(NULL)
, cache_size(0)
{}
//for each texformat, multiplier from numtexels to numbytes (fixed point 30.2)
const int texSizes[] = {0, 4, 1, 2, 4, 1, 4, 8};
ADPCMCacheItem *list_front, *list_back;
//used to hold a copy of the palette specified for this texture
u16 pal[256];
//this ought to be enough for anyone
static const u32 kMaxCacheSize = 64*1024*1024;
//this is not really precise, it is off by a constant factor
u32 cache_size;
u32 *dwdst = (u32*)TexCache_texMAP;
u32 textureMode = (unsigned short)((format>>26)&0x07);
unsigned int sizeX=(8 << ((format>>20)&0x07));
unsigned int sizeY=(8 << ((format>>23)&0x07));
unsigned int imageSize = sizeX*sizeY;
u8 *adr;
u32 paletteAddress;
switch (textureMode)
{
case TEXMODE_I2:
paletteAddress = texpal<<3;
break;
case TEXMODE_A3I5: //a3i5
case TEXMODE_I4: //i4
case TEXMODE_I8: //i8
case TEXMODE_A5I3: //a5i3
case TEXMODE_16BPP: //16bpp
case TEXMODE_4X4: //4x4
default:
paletteAddress = texpal<<4;
break;
void list_remove(ADPCMCacheItem* item) {
if(item->next) item->next->prev = item->prev;
if(item->prev) item->prev->next = item->next;
if(item == list_front) list_front = item->next;
if(item == list_back) list_back = item->prev;
}
//analyze the texture memory mapping and the specifications of this texture
int palSize = palSizes[textureMode];
int texSize = (imageSize*texSizes[textureMode])>>2; //shifted because the texSizes multiplier is fixed point
MemSpan ms = MemSpan_TexMem((format&0xFFFF)<<3,texSize);
MemSpan mspal = MemSpan_TexPalette(paletteAddress,palSize*2);
//determine the location for 4x4 index data
u32 indexBase;
if((format & 0xc000) == 0x8000) indexBase = 0x30000;
else indexBase = 0x20000;
u32 indexOffset = (format&0x3FFF)<<2;
int indexSize = 0;
MemSpan msIndex;
if(textureMode == TEXMODE_4X4)
void list_push_front(ADPCMCacheItem* item)
{
indexSize = imageSize>>3;
msIndex = MemSpan_TexMem(indexOffset+indexBase,indexSize);
item->next = list_front;
if(list_front) list_front->prev = item;
else list_back = item;
item->prev = NULL;
list_front = item;
}
//dump the palette to a temp buffer, so that we don't have to worry about memory mapping.
//this isnt such a problem with texture memory, because we read sequentially from it.
//however, we read randomly from palette memory, so the mapping is more costly.
#ifdef WORDS_BIGENDIAN
mspal.dump16(pal);
#else
mspal.dump(pal);
#endif
u32 tx=texcache_start;
//if(false)
while (TRUE)
template<TexCache_TexFormat TEXFORMAT>
ADPCMCacheItem* scan(u32 format, u32 texpal)
{
//conditions where we give up and regenerate the texture:
if (texcache_stop == tx) break;
if (texcache[tx].frm == 0) break;
//for each texformat, number of palette entries
static const int palSizes[] = {0, 32, 4, 16, 256, 0, 8, 0};
//conditions where we reject matches:
//when the teximage or texpal params dont match
//(this is our key for identifying palettes in the cache)
if (texcache[tx].frm != format) goto REJECT;
if (texcache[tx].pal != texpal) goto REJECT;
//for each texformat, multiplier from numtexels to numbytes (fixed point 30.2)
static const int texSizes[] = {0, 4, 1, 2, 4, 1, 4, 8};
//the texture matches params, but isnt suspected invalid. accept it.
if (!texcache[tx].suspectedInvalid) goto ACCEPT;
//used to hold a copy of the palette specified for this texture
u16 pal[256];
//if we couldnt cache this entire texture due to it being too large, then reject it
if (texSize+indexSize > (int)sizeof(texcache[tx].dump.texture)) goto REJECT;
u32 textureMode = (unsigned short)((format>>26)&0x07);
u32 sizeX=(8 << ((format>>20)&0x07));
u32 sizeY=(8 << ((format>>23)&0x07));
u32 imageSize = sizeX*sizeY;
//when the palettes dont match:
//note that we are considering 4x4 textures to have a palette size of 0.
//they really have a potentially HUGE palette, too big for us to handle like a normal palette,
//so they go through a different system
if (mspal.size != 0 && memcmp(texcache[tx].dump.palette,pal,mspal.size)) goto REJECT;
u8 *adr;
//when the texture data doesn't match
if(ms.memcmp(texcache[tx].dump.texture,sizeof(texcache[tx].dump.texture))) goto REJECT;
u32 paletteAddress;
//if the texture is 4x4 then the index data must match
switch (textureMode)
{
case TEXMODE_I2:
paletteAddress = texpal<<3;
break;
case TEXMODE_A3I5: //a3i5
case TEXMODE_I4: //i4
case TEXMODE_I8: //i8
case TEXMODE_A5I3: //a5i3
case TEXMODE_16BPP: //16bpp
case TEXMODE_4X4: //4x4
default:
paletteAddress = texpal<<4;
break;
}
//analyze the texture memory mapping and the specifications of this texture
int palSize = palSizes[textureMode];
int texSize = (imageSize*texSizes[textureMode])>>2; //shifted because the texSizes multiplier is fixed point
MemSpan ms = MemSpan_TexMem((format&0xFFFF)<<3,texSize);
MemSpan mspal = MemSpan_TexPalette(paletteAddress,palSize*2);
//determine the location for 4x4 index data
u32 indexBase;
if((format & 0xc000) == 0x8000) indexBase = 0x30000;
else indexBase = 0x20000;
u32 indexOffset = (format&0x3FFF)<<2;
int indexSize = 0;
MemSpan msIndex;
if(textureMode == TEXMODE_4X4)
{
if(msIndex.memcmp(texcache[tx].dump.texture + texcache[tx].dump.textureSize,texcache[tx].dump.indexSize)) goto REJECT;
indexSize = imageSize>>3;
msIndex = MemSpan_TexMem(indexOffset+indexBase,indexSize);
}
ACCEPT:
texcache[tx].suspectedInvalid = false;
if(lastTexture == -1 || (int)tx != lastTexture)
//dump the palette to a temp buffer, so that we don't have to worry about memory mapping.
//this isnt such a problem with texture memory, because we read sequentially from it.
//however, we read randomly from palette memory, so the mapping is more costly.
#ifdef WORDS_BIGENDIAN
mspal.dump16(pal);
#else
mspal.dump(pal);
#endif
for(ADPCMCacheItem* curr = list_front;curr;curr=curr->next)
{
lastTexture = tx;
if(TexCache_BindTexture)
TexCache_BindTexture(tx);
}
return;
REJECT:
tx++;
if ( tx > MAX_TEXTURE )
{
texcache_stop=texcache_start;
texcache[texcache_stop].frm=0;
texcache_start++;
if (texcache_start>MAX_TEXTURE)
//conditions where we reject matches:
//when the teximage or texpal params dont match
//(this is our key for identifying textures in the cache)
if(curr->texformat != format) continue;
if(curr->texpal != texpal) continue;
//we're being asked for a different format than what we had cached.
if(curr->cacheFormat != TEXFORMAT) goto REJECT;
//not used anymore -- add another method to purge suspicious items from the cache
//the texture matches params, but isnt suspected invalid. accept it.
if (!curr->suspectedInvalid) return curr;
//when the palettes dont match:
//note that we are considering 4x4 textures to have a palette size of 0.
//they really have a potentially HUGE palette, too big for us to handle like a normal palette,
//so they go through a different system
if(mspal.size != 0 && memcmp(curr->dump.palette,pal,mspal.size)) goto REJECT;
//when the texture data doesn't match
if(ms.memcmp(curr->dump.texture,sizeof(curr->dump.texture))) goto REJECT;
//if the texture is 4x4 then the index data must match
if(textureMode == TEXMODE_4X4)
{
texcache_start=0;
texcache_stop=MAX_TEXTURE<<1;
}
tx=0;
}
}
lastTexture = tx;
//glBindTexture(GL_TEXTURE_2D, texcache[tx].id);
texcache[tx].suspectedInvalid = false;
texcache[tx].frm=format;
texcache[tx].mode=textureMode;
texcache[tx].pal=texpal;
texcache[tx].sizeX=sizeX;
texcache[tx].sizeY=sizeY;
texcache[tx].invSizeX=1.0f/((float)(sizeX));
texcache[tx].invSizeY=1.0f/((float)(sizeY));
texcache[tx].dump.textureSize = ms.dump(texcache[tx].dump.texture,sizeof(texcache[tx].dump.texture));
//dump palette data for cache keying
if ( palSize )
{
memcpy(texcache[tx].dump.palette, pal, palSize*2);
}
//dump 4x4 index data for cache keying
texcache[tx].dump.indexSize = 0;
if(textureMode == TEXMODE_4X4)
{
texcache[tx].dump.indexSize = min(msIndex.size,(int)sizeof(texcache[tx].dump.texture) - texcache[tx].dump.textureSize);
msIndex.dump(texcache[tx].dump.texture+texcache[tx].dump.textureSize,texcache[tx].dump.indexSize);
}
//INFO("Texture %03i - format=%08X; pal=%04X (mode %X, width %04i, height %04i)\n",i, texcache[i].frm, texcache[i].pal, texcache[i].mode, sizeX, sizeY);
//============================================================================ Texture conversion
const u32 opaqueColor = TEXFORMAT==TexFormat_32bpp?255:31;
u32 palZeroTransparent = (1-((format>>29)&1))*opaqueColor;
switch (texcache[tx].mode)
{
case TEXMODE_A3I5:
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
u16 c = pal[*adr&31];
u8 alpha = *adr>>5;
if(TEXFORMAT == TexFormat_15bpp)
*dwdst++ = RGB15TO6665(c,material_3bit_to_5bit[alpha]);
else
*dwdst++ = RGB15TO32(c,material_3bit_to_8bit[alpha]);
adr++;
}
if(msIndex.memcmp(curr->dump.texture + curr->dump.textureSize,curr->dump.indexSize)) goto REJECT;
}
//we found a match. just return it
//curr->lock();
list_remove(curr);
list_push_front(curr);
return curr;
REJECT:
//we found a cached item for the current address, but the data is stale.
//for a variety of complicated reasons, we need to throw it out right this instant.
list_remove(curr);
delete curr;
break;
}
case TEXMODE_I2:
//item was not found. recruit an existing one (the oldest), or create a new one
//evict(); //reduce the size of the cache if necessary
//TODO - as a peculiarity of the texcache, eviction must happen after the entire 3d frame runs
//to support separate cache and read passes
ADPCMCacheItem* newitem = new ADPCMCacheItem();
list_push_front(newitem);
//newitem->lock();
newitem->suspectedInvalid = false;
newitem->texformat = format;
newitem->cacheFormat = TEXFORMAT;
newitem->texpal = texpal;
newitem->sizeX=sizeX;
newitem->sizeY=sizeY;
newitem->invSizeX=1.0f/((float)(sizeX));
newitem->invSizeY=1.0f/((float)(sizeY));
newitem->dump.textureSize = ms.dump(newitem->dump.texture,sizeof(newitem->dump.texture));
newitem->decode_len = sizeX*sizeY*4;
newitem->mode = textureMode;
cache_size += newitem->decode_len;
newitem->decoded = new u8[newitem->decode_len];
u32 *dwdst = (u32*)newitem->decoded;
//dump palette data for cache keying
if(palSize)
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
u8 bits;
u16 c;
bits = (*adr)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
bits = ((*adr)>>2)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
bits = ((*adr)>>4)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
bits = ((*adr)>>6)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
adr++;
}
}
break;
memcpy(newitem->dump.palette, pal, palSize*2);
}
case TEXMODE_I4:
//dump 4x4 index data for cache keying
newitem->dump.indexSize = 0;
if(textureMode == TEXMODE_4X4)
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
u8 bits;
u16 c;
bits = (*adr)&0xF;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
bits = ((*adr)>>4);
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
adr++;
}
}
break;
newitem->dump.indexSize = min(msIndex.size,(int)sizeof(newitem->dump.texture) - newitem->dump.textureSize);
msIndex.dump(newitem->dump.texture+newitem->dump.textureSize,newitem->dump.indexSize);
}
case TEXMODE_I8:
//============================================================================
//Texture conversion
//============================================================================
const u32 opaqueColor = TEXFORMAT==TexFormat_32bpp?255:31;
u32 palZeroTransparent = (1-((format>>29)&1))*opaqueColor;
switch (newitem->mode)
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; ++x)
{
u16 c = pal[*adr];
*dwdst++ = CONVERT(c,(*adr == 0) ? palZeroTransparent : opaqueColor);
adr++;
}
}
}
break;
case TEXMODE_4X4:
{
//RGB16TO32 is used here because the other conversion macros result in broken interpolation logic
if(ms.numItems != 1) {
PROGINFO("Your 4x4 texture has overrun its texture slot.\n");
}
//this check isnt necessary since the addressing is tied to the texture data which will also run out:
//if(msIndex.numItems != 1) PROGINFO("Your 4x4 texture index has overrun its slot.\n");
#define PAL4X4(offset) ( *(u16*)( MMU.texInfo.texPalSlot[((paletteAddress + (offset)*2)>>14)] + ((paletteAddress + (offset)*2)&0x3FFF) ) )
u16* slot1;
u32* map = (u32*)ms.items[0].ptr;
u32 limit = ms.items[0].len<<2;
u32 d = 0;
if ( (texcache[tx].frm & 0xc000) == 0x8000)
// texel are in slot 2
slot1=(u16*)&MMU.texInfo.textureSlotAddr[1][((texcache[tx].frm & 0x3FFF)<<2)+0x010000];
else
slot1=(u16*)&MMU.texInfo.textureSlotAddr[1][(texcache[tx].frm & 0x3FFF)<<2];
u16 yTmpSize = (texcache[tx].sizeY>>2);
u16 xTmpSize = (texcache[tx].sizeX>>2);
//this is flagged whenever a 4x4 overruns its slot.
//i am guessing we just generate black in that case
bool dead = false;
for (int y = 0; y < yTmpSize; y ++)
case TEXMODE_A3I5:
{
u32 tmpPos[4]={(y<<2)*texcache[tx].sizeX,((y<<2)+1)*texcache[tx].sizeX,
((y<<2)+2)*texcache[tx].sizeX,((y<<2)+3)*texcache[tx].sizeX};
for (int x = 0; x < xTmpSize; x ++, d++)
{
if(d >= limit)
dead = true;
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
u16 c = pal[*adr&31];
u8 alpha = *adr>>5;
if(TEXFORMAT == TexFormat_15bpp)
*dwdst++ = RGB15TO6665(c,material_3bit_to_5bit[alpha]);
else
*dwdst++ = RGB15TO32(c,material_3bit_to_8bit[alpha]);
adr++;
}
}
break;
}
if(dead) {
case TEXMODE_I2:
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
u8 bits;
u16 c;
bits = (*adr)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
bits = ((*adr)>>2)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
bits = ((*adr)>>4)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
bits = ((*adr)>>6)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
adr++;
}
}
break;
}
case TEXMODE_I4:
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
u8 bits;
u16 c;
bits = (*adr)&0xF;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
bits = ((*adr)>>4);
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
adr++;
}
}
break;
}
case TEXMODE_I8:
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; ++x)
{
u16 c = pal[*adr];
*dwdst++ = CONVERT(c,(*adr == 0) ? palZeroTransparent : opaqueColor);
adr++;
}
}
}
break;
case TEXMODE_4X4:
{
//RGB16TO32 is used here because the other conversion macros result in broken interpolation logic
if(ms.numItems != 1) {
PROGINFO("Your 4x4 texture has overrun its texture slot.\n");
}
//this check isnt necessary since the addressing is tied to the texture data which will also run out:
//if(msIndex.numItems != 1) PROGINFO("Your 4x4 texture index has overrun its slot.\n");
#define PAL4X4(offset) ( *(u16*)( MMU.texInfo.texPalSlot[((paletteAddress + (offset)*2)>>14)] + ((paletteAddress + (offset)*2)&0x3FFF) ) )
u16* slot1;
u32* map = (u32*)ms.items[0].ptr;
u32 limit = ms.items[0].len<<2;
u32 d = 0;
if ( (format & 0xc000) == 0x8000)
// texel are in slot 2
slot1=(u16*)&MMU.texInfo.textureSlotAddr[1][((format & 0x3FFF)<<2)+0x010000];
else
slot1=(u16*)&MMU.texInfo.textureSlotAddr[1][(format & 0x3FFF)<<2];
u16 yTmpSize = (sizeY>>2);
u16 xTmpSize = (sizeX>>2);
//this is flagged whenever a 4x4 overruns its slot.
//i am guessing we just generate black in that case
bool dead = false;
for (int y = 0; y < yTmpSize; y ++)
{
u32 tmpPos[4]={(y<<2)*sizeX,((y<<2)+1)*sizeX,
((y<<2)+2)*sizeX,((y<<2)+3)*sizeX};
for (int x = 0; x < xTmpSize; x ++, d++)
{
if(d >= limit)
dead = true;
if(dead) {
for (int sy = 0; sy < 4; sy++)
{
u32 currentPos = (x<<2) + tmpPos[sy];
dwdst[currentPos] = dwdst[currentPos+1] = dwdst[currentPos+2] = dwdst[currentPos+3] = 0;
}
continue;
}
u32 currBlock = map[d];
u16 pal1 = slot1[d];
u16 pal1offset = (pal1 & 0x3FFF)<<1;
u8 mode = pal1>>14;
u32 tmp_col[4];
tmp_col[0]=RGB16TO32(PAL4X4(pal1offset),255);
tmp_col[1]=RGB16TO32(PAL4X4(pal1offset+1),255);
switch (mode)
{
case 0:
tmp_col[2]=RGB16TO32(PAL4X4(pal1offset+2),255);
tmp_col[3]=RGB16TO32(0x7FFF,0);
break;
case 1:
tmp_col[2]=(((tmp_col[0]&0xFF)+(tmp_col[1]&0xff))>>1)|
(((tmp_col[0]&(0xFF<<8))+(tmp_col[1]&(0xFF<<8)))>>1)|
(((tmp_col[0]&(0xFF<<16))+(tmp_col[1]&(0xFF<<16)))>>1)|
(0xff<<24);
tmp_col[3]=RGB16TO32(0x7FFF,0);
break;
case 2:
tmp_col[2]=RGB16TO32(PAL4X4(pal1offset+2),255);
tmp_col[3]=RGB16TO32(PAL4X4(pal1offset+3),255);
break;
case 3:
{
u32 red1, red2;
u32 green1, green2;
u32 blue1, blue2;
u16 tmp1, tmp2;
red1=tmp_col[0]&0xff;
green1=(tmp_col[0]>>8)&0xff;
blue1=(tmp_col[0]>>16)&0xff;
red2=tmp_col[1]&0xff;
green2=(tmp_col[1]>>8)&0xff;
blue2=(tmp_col[1]>>16)&0xff;
tmp1=((red1*5+red2*3)>>6)|
(((green1*5+green2*3)>>6)<<5)|
(((blue1*5+blue2*3)>>6)<<10);
tmp2=((red2*5+red1*3)>>6)|
(((green2*5+green1*3)>>6)<<5)|
(((blue2*5+blue1*3)>>6)<<10);
tmp_col[2]=RGB16TO32(tmp1,255);
tmp_col[3]=RGB16TO32(tmp2,255);
break;
}
}
if(TEXFORMAT==TexFormat_15bpp)
{
for(int i=0;i<4;i++)
{
tmp_col[i] >>= 2;
tmp_col[i] &= 0x3F3F3F3F;
u32 a = tmp_col[i]>>24;
tmp_col[i] &= 0x00FFFFFF;
tmp_col[i] |= (a>>1)<<24;
}
}
//TODO - this could be more precise for 32bpp mode (run it through the color separation table)
//set all 16 texels
for (int sy = 0; sy < 4; sy++)
{
// Texture offset
u32 currentPos = (x<<2) + tmpPos[sy];
dwdst[currentPos] = dwdst[currentPos+1] = dwdst[currentPos+2] = dwdst[currentPos+3] = 0;
u8 currRow = (u8)((currBlock>>(sy<<3))&0xFF);
dwdst[currentPos] = tmp_col[currRow&3];
dwdst[currentPos+1] = tmp_col[(currRow>>2)&3];
dwdst[currentPos+2] = tmp_col[(currRow>>4)&3];
dwdst[currentPos+3] = tmp_col[(currRow>>6)&3];
}
continue;
}
u32 currBlock = map[d];
u16 pal1 = slot1[d];
u16 pal1offset = (pal1 & 0x3FFF)<<1;
u8 mode = pal1>>14;
u32 tmp_col[4];
tmp_col[0]=RGB16TO32(PAL4X4(pal1offset),255);
tmp_col[1]=RGB16TO32(PAL4X4(pal1offset+1),255);
switch (mode)
{
case 0:
tmp_col[2]=RGB16TO32(PAL4X4(pal1offset+2),255);
tmp_col[3]=RGB16TO32(0x7FFF,0);
break;
case 1:
tmp_col[2]=(((tmp_col[0]&0xFF)+(tmp_col[1]&0xff))>>1)|
(((tmp_col[0]&(0xFF<<8))+(tmp_col[1]&(0xFF<<8)))>>1)|
(((tmp_col[0]&(0xFF<<16))+(tmp_col[1]&(0xFF<<16)))>>1)|
(0xff<<24);
tmp_col[3]=RGB16TO32(0x7FFF,0);
break;
case 2:
tmp_col[2]=RGB16TO32(PAL4X4(pal1offset+2),255);
tmp_col[3]=RGB16TO32(PAL4X4(pal1offset+3),255);
break;
case 3:
{
u32 red1, red2;
u32 green1, green2;
u32 blue1, blue2;
u16 tmp1, tmp2;
red1=tmp_col[0]&0xff;
green1=(tmp_col[0]>>8)&0xff;
blue1=(tmp_col[0]>>16)&0xff;
red2=tmp_col[1]&0xff;
green2=(tmp_col[1]>>8)&0xff;
blue2=(tmp_col[1]>>16)&0xff;
tmp1=((red1*5+red2*3)>>6)|
(((green1*5+green2*3)>>6)<<5)|
(((blue1*5+blue2*3)>>6)<<10);
tmp2=((red2*5+red1*3)>>6)|
(((green2*5+green1*3)>>6)<<5)|
(((blue2*5+blue1*3)>>6)<<10);
tmp_col[2]=RGB16TO32(tmp1,255);
tmp_col[3]=RGB16TO32(tmp2,255);
break;
}
}
if(TEXFORMAT==TexFormat_15bpp)
{
for(int i=0;i<4;i++)
{
tmp_col[i] >>= 2;
tmp_col[i] &= 0x3F3F3F3F;
u32 a = tmp_col[i]>>24;
tmp_col[i] &= 0x00FFFFFF;
tmp_col[i] |= (a>>1)<<24;
}
}
//TODO - this could be more precise for 32bpp mode (run it through the color separation table)
//set all 16 texels
for (int sy = 0; sy < 4; sy++)
{
// Texture offset
u32 currentPos = (x<<2) + tmpPos[sy];
u8 currRow = (u8)((currBlock>>(sy<<3))&0xFF);
dwdst[currentPos] = tmp_col[currRow&3];
dwdst[currentPos+1] = tmp_col[(currRow>>2)&3];
dwdst[currentPos+2] = tmp_col[(currRow>>4)&3];
dwdst[currentPos+3] = tmp_col[(currRow>>6)&3];
}
}
break;
}
break;
}
case TEXMODE_A5I3:
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; ++x)
{
u16 c = pal[*adr&0x07];
u8 alpha = (*adr>>3);
if(TEXFORMAT == TexFormat_15bpp)
*dwdst++ = RGB15TO6665(c,alpha);
else
*dwdst++ = RGB15TO32(c,material_5bit_to_8bit[alpha]);
adr++;
case TEXMODE_A5I3:
{
for(int j=0;j<ms.numItems;j++) {
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; ++x)
{
u16 c = pal[*adr&0x07];
u8 alpha = (*adr>>3);
if(TEXFORMAT == TexFormat_15bpp)
*dwdst++ = RGB15TO6665(c,alpha);
else
*dwdst++ = RGB15TO32(c,material_5bit_to_8bit[alpha]);
adr++;
}
}
break;
}
break;
}
case TEXMODE_16BPP:
{
for(int j=0;j<ms.numItems;j++) {
u16* map = (u16*)ms.items[j].ptr;
int len = ms.items[j].len>>1;
for(int x = 0; x < len; ++x)
{
u16 c = map[x];
int alpha = ((c&0x8000)?opaqueColor:0);
*dwdst++ = CONVERT(c&0x7FFF,alpha);
case TEXMODE_16BPP:
{
for(int j=0;j<ms.numItems;j++) {
u16* map = (u16*)ms.items[j].ptr;
int len = ms.items[j].len>>1;
for(int x = 0; x < len; ++x)
{
u16 c = map[x];
int alpha = ((c&0x8000)?opaqueColor:0);
*dwdst++ = CONVERT(c&0x7FFF,alpha);
}
}
break;
}
break;
}
}
} //switch(texture format)
if(TexCache_BindTextureData != 0)
TexCache_BindTextureData(tx,TexCache_texMAP);
/*if(user)
user->BindTextureData(tx,TexCache_texMAP);
#ifdef DO_DEBUG_DUMP_TEXTURE
DebugDumpTexture(tx);
#endif
#endif*/
}
return newitem;
} //scan()
void evict(const u32 target = kMaxCacheSize) {
//evicts old cache items until it is less than the max cache size
//this means we actually can exceed the cache by the size of the next item.
//if we really wanted to hold ourselves to it, we could evict to kMaxCacheSize-nextItemSize
while(cache_size > target)
{
ADPCMCacheItem *oldest = list_back;
while(oldest && oldest->lockCount>0) oldest = oldest->prev; //find an unlocked one
if(!oldest)
{
//nothing we can do, everything in the cache is locked. maybe we're leaking.
//just quit trying to evict
return;
}
list_remove(oldest);
cache_size -= oldest->decode_len;
//printf("evicting! totalsize:%d\n",cache_size);
delete oldest;
}
}
} adpcmCache;
void TexCache_Reset()
{
if(TexCache_texMAP == NULL) TexCache_texMAP = new u8[1024*2048*4];
if(texcache == NULL) texcache = new TextureCache[MAX_TEXTURE+1];
//if(TexCache_texMAP == NULL) TexCache_texMAP = new u8[1024*2048*4];
//if(texcache == NULL) texcache = new TextureCache[MAX_TEXTURE+1];
memset(texcache,0,sizeof(TextureCache[MAX_TEXTURE+1]));
//memset(texcache,0,sizeof(TextureCache[MAX_TEXTURE+1]));
texcache_start=0;
texcache_stop=MAX_TEXTURE<<1;
}
TextureCache* TexCache_Curr()
{
if(lastTexture == -1)
return NULL;
else return &texcache[lastTexture];
//texcache_start=0;
//texcache_stop=MAX_TEXTURE<<1;
adpcmCache.evict(0);
}
void TexCache_Invalidate()
{
//well, this is a very blunt instrument.
//lets just flag all the textures as invalid.
for(int i=0;i<MAX_TEXTURE+1;i++) {
texcache[i].suspectedInvalid = true;
////well, this is a very blunt instrument.
////lets just flag all the textures as invalid.
//for(int i=0;i<MAX_TEXTURE+1;i++) {
// texcache[i].suspectedInvalid = true;
//invalidate all 4x4 textures when texture palettes change mappings
//this is necessary because we arent tracking 4x4 texture palettes to look for changes.
//Although I concede this is a bit paranoid.. I think the odds of anyone changing 4x4 palette data
//without also changing the texture data is pretty much zero.
//
//TODO - move this to a separate signal: split into TexReconfigureSignal and TexPaletteReconfigureSignal
if(texcache[i].mode == TEXMODE_4X4)
texcache[i].frm = 0;
// //invalidate all 4x4 textures when texture palettes change mappings
// //this is necessary because we arent tracking 4x4 texture palettes to look for changes.
// //Although I concede this is a bit paranoid.. I think the odds of anyone changing 4x4 palette data
// //without also changing the texture data is pretty much zero.
// //
// //TODO - move this to a separate signal: split into TexReconfigureSignal and TexPaletteReconfigureSignal
// if(texcache[i].mode == TEXMODE_4X4)
// texcache[i].frm = 0;
//}
adpcmCache.evict(0);
}
ADPCMCacheItem* TexCache_SetTexture(TexCache_TexFormat TEXFORMAT, u32 format, u32 texpal)
{
switch(TEXFORMAT)
{
case TexFormat_32bpp: return adpcmCache.scan<TexFormat_32bpp>(format,texpal);
case TexFormat_15bpp: return adpcmCache.scan<TexFormat_15bpp>(format,texpal);
default: assert(false); return NULL;
}
}
void (*TexCache_BindTexture)(u32 texnum) = NULL;
void (*TexCache_BindTextureData)(u32 texnum, u8* data);
//these templates needed to be instantiated manually
template void TexCache_SetTexture<TexFormat_32bpp>(u32 format, u32 texpal);
template void TexCache_SetTexture<TexFormat_15bpp>(u32 format, u32 texpal);
//call this periodically to keep the tex cache clean
void TexCache_EvictFrame()
{
adpcmCache.evict();
}

View File

@ -5,47 +5,62 @@
enum TexCache_TexFormat
{
TexFormat_32bpp,
TexFormat_15bpp
TexFormat_None, //used when nothing yet is cached
TexFormat_32bpp, //used by ogl renderer
TexFormat_15bpp //used by rasterizer
};
#define MAX_TEXTURE 500
struct CACHE_ALIGN TextureCache
class ADPCMCacheItem
{
u32 id;
u32 frm;
u32 mode;
u32 pal;
u32 sizeX;
u32 sizeY;
float invSizeX;
float invSizeY;
public:
ADPCMCacheItem()
: decoded(NULL)
, decode_len(0)
, next(NULL)
, prev(NULL)
, lockCount(0)
, cacheFormat(TexFormat_None)
, deleteCallback(NULL)
, suspectedInvalid(false)
{}
~ADPCMCacheItem() {
delete[] decoded;
if(deleteCallback) deleteCallback(this);
}
void unlock() {
lockCount--;
}
void lock() {
lockCount++;
}
u32 decode_len;
u32 mode;
u8* decoded; //decoded texture data
ADPCMCacheItem *next, *prev; //double linked list
int lockCount;
bool suspectedInvalid;
u32 texformat, texpal;
u32 sizeX, sizeY;
float invSizeX, invSizeY;
void* texid; //used by ogl renderer for the texid
void (*deleteCallback)(ADPCMCacheItem*);
TexCache_TexFormat cacheFormat;
//TODO - this is a little wasteful
struct {
int textureSize, indexSize;
u8 texture[128*1024]; // 128Kb texture slot
u8 palette[256*2];
int textureSize, indexSize;
u8 texture[128*1024]; // 128Kb texture slot
u8 palette[256*2];
} dump;
//set if this texture is suspected be invalid due to a vram reconfigure
bool suspectedInvalid;
};
extern TextureCache *texcache;
extern void (*TexCache_BindTexture)(u32 texnum);
extern void (*TexCache_BindTextureData)(u32 texnum, u8* data);
void TexCache_Reset();
template<TexCache_TexFormat>
void TexCache_SetTexture(u32 format, u32 texpal);
void TexCache_Invalidate();
void TexCache_Reset();
void TexCache_EvictFrame();
extern u8 *TexCache_texMAP;
TextureCache* TexCache_Curr();
ADPCMCacheItem* TexCache_SetTexture(TexCache_TexFormat TEXFORMAT, u32 format, u32 texpal);
#endif

279
desmume/src/utils/task.cpp Normal file
View File

@ -0,0 +1,279 @@
/* Copyright 2009 DeSmuME team
This file is part of DeSmuME
DeSmuME is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
DeSmuME 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 for more details.
You should have received a copy of the GNU General Public License
along with DeSmuME; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "task.h"
#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
class Task::Impl {
public:
Impl();
~Impl();
bool spinlock;
void start(bool spinlock);
//execute some work
void execute(const TWork &work, void* param);
//wait for the work to complete
void* finish();
static DWORD __stdcall s_taskProc(void *ptr);
void taskProc();
void init();
//the work function that shall be executed
TWork work;
void* param;
HANDLE incomingWork, workDone, hThread;
volatile bool bIncomingWork, bWorkDone, bKill;
bool bStarted;
};
static void* killTask(void* task)
{
((Task::Impl*)task)->bKill = true;
return 0;
}
Task::Impl::~Impl()
{
if(!bStarted) return;
execute(killTask,this);
finish();
CloseHandle(incomingWork);
CloseHandle(workDone);
CloseHandle(hThread);
}
Task::Impl::Impl()
: work(NULL)
, bIncomingWork(false)
, bWorkDone(true)
, bKill(false)
, bStarted(false)
{
}
DWORD __stdcall Task::Impl::s_taskProc(void *ptr)
{
//just past the buck to the instance method
((Task::Impl*)ptr)->taskProc();
return 0;
}
void Task::Impl::taskProc()
{
for(;;) {
if(bKill) break;
//wait for a chunk of work
if(spinlock) while(!bIncomingWork) Sleep(0);
else WaitForSingleObject(incomingWork,INFINITE);
bIncomingWork = false;
//execute the work
param = work(param);
//signal completion
if(!spinlock) SetEvent(workDone);
bWorkDone = true;
}
}
void Task::Impl::start(bool spinlock)
{
bStarted = true;
this->spinlock = spinlock;
incomingWork = CreateEvent(NULL,FALSE,FALSE,NULL);
workDone = CreateEvent(NULL,FALSE,FALSE,NULL);
hThread = CreateThread(NULL,0,Task::Impl::s_taskProc,(void*)this, 0, NULL);
}
void Task::Impl::execute(const TWork &work, void* param)
{
//setup the work
this->work = work;
this->param = param;
bWorkDone = false;
//signal it to start
if(!spinlock) SetEvent(incomingWork);
bIncomingWork = true;
}
void* Task::Impl::finish()
{
//just wait for the work to be done
if(spinlock)
while(!bWorkDone)
Sleep(0);
else WaitForSingleObject(workDone,INFINITE);
return param;
}
#else
//just a stub impl that doesnt actually do any threading.
//somebody needs to update the pthread implementation below
class Task::Impl {
public:
Impl() {}
~Impl() {}
void start(bool spinlock) {}
void* ret;
void execute(const TWork &work, void* param) { ret = work(param); }
void* finish() { return ret; }
};
/*
#include <pthread.h>
class Task::Impl {
public:
Impl();
//execute some work
void execute(const TWork &work, void* param);
//wait for the work to complete
void* finish();
pthread_t thread;
static void* s_taskProc(void *ptr);
void taskProc();
void init();
//the work function that shall be executed
TWork work;
void* param;
bool initialized;
struct WaitEvent
{
WaitEvent()
: condition(PTHREAD_COND_INITIALIZER)
, mutex(PTHREAD_MUTEX_INITIALIZER)
, value(false)
{}
pthread_mutex_t mutex;
pthread_cond_t condition;
bool value;
//waits for the WaitEvent to be set
void waitAndClear()
{
lock();
if(!value)
pthread_cond_wait( &condition, &mutex );
value = false;
unlock();
}
//sets the WaitEvent
void signal()
{
lock();
if(!value) {
value = true;
pthread_cond_signal( &condition );
}
unlock();
}
//locks the condition's mutex
void lock() { pthread_mutex_lock(&mutex); }
//unlocks the condition's mutex
void unlock() { pthread_mutex_unlock( &mutex ); }
} incomingWork, workDone;
};
Task::Impl::Impl()
: work(NULL)
, initialized(false)
{
}
void* Task::Impl::s_taskProc(void *ptr)
{
//just past the buck to the instance method
((Task::Impl*)ptr)->taskProc();
return 0;
}
void Task::Impl::taskProc()
{
for(;;) {
//wait for a chunk of work
incomingWork.waitAndClear();
//execute the work
param = work(param);
//signal completion
workDone.signal();
}
}
void Task::Impl::init()
{
pthread_create( &thread, NULL, Task::Impl::s_taskProc, (void*)this );
initialized = true;
}
void Task::Impl::execute(const TWork &work, void* param)
{
//initialization is deferred to the first execute to give win32 time to startup
if(!initialized) init();
//setup the work
this->work = work;
this->param = param;
//signal it to start
incomingWork.signal();
}
void* Task::Impl::finish()
{
//just wait for the work to be done
workDone.waitAndClear();
return param;
}
*/
#endif
void Task::start(bool spinlock) { impl->start(spinlock); }
Task::Task() : impl(new Task::Impl()) {}
Task::~Task() { delete impl; }
void Task::execute(const TWork &work, void* param) { impl->execute(work,param); }
void* Task::finish() { return impl->finish(); }

46
desmume/src/utils/task.h Normal file
View File

@ -0,0 +1,46 @@
/* Copyright 2009 DeSmuME team
This file is part of DeSmuME
DeSmuME is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
DeSmuME 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 for more details.
You should have received a copy of the GNU General Public License
along with DeSmuME; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TASK_H_
//Sort of like a single-thread thread pool.
//You hand it a worker function and then call finish() to synch with its completion
class Task
{
public:
Task();
~Task();
typedef void * (*TWork)(void *);
void start(bool spinlock);
//execute some work
void execute(const TWork &work, void* param);
//wait for the work to complete
void* finish();
class Impl;
Impl *impl;
};
#endif

View File

@ -643,10 +643,6 @@
RelativePath=".\aviout.h"
>
</File>
<File
RelativePath=".\buildconfig.h"
>
</File>
<File
RelativePath=".\cheatsWin.cpp"
>
@ -971,6 +967,14 @@
RelativePath="..\utils\md5.h"
>
</File>
<File
RelativePath="..\utils\task.cpp"
>
</File>
<File
RelativePath="..\utils\task.h"
>
</File>
<File
RelativePath="..\utils\valuearray.h"
>

View File

@ -1019,6 +1019,14 @@
RelativePath="..\utils\md5.h"
>
</File>
<File
RelativePath="..\utils\task.cpp"
>
</File>
<File
RelativePath="..\utils\task.h"
>
</File>
<File
RelativePath="..\utils\valuearray.h"
>

View File

@ -1002,7 +1002,7 @@ static void DoDisplay(bool firstTime)
//on single core systems, draw straight to the screen
//we only do this once per emulated frame because we don't want to waste time redrawing
//on such lousy computers
if(CommonSettings.single_core)
if(CommonSettings.single_core())
{
aggDraw.hud->attach((u8*)video.buffer, 256, 384, 1024);
DoDisplay_DrawHud();
@ -1025,7 +1025,7 @@ static void DoDisplay(bool firstTime)
//apply user's filter
video.filter();
if(!CommonSettings.single_core)
if(!CommonSettings.single_core())
{
//draw and composite the OSD (but not if we are drawing osd straight to screen)
DoDisplay_DrawHud();
@ -1081,7 +1081,7 @@ void KillDisplay()
void Display()
{
if(CommonSettings.single_core)
if(CommonSettings.single_core())
{
video.srcBuffer = (u8*)GPU_screen;
DoDisplay(true);
@ -1229,7 +1229,7 @@ static void StepRunLoop_Paused()
Sleep(100);
// periodically update single-core OSD when paused and in the foreground
if(CommonSettings.single_core && GetActiveWindow() == mainLoopData.hwnd)
if(CommonSettings.single_core() && GetActiveWindow() == mainLoopData.hwnd)
{
video.srcBuffer = (u8*)GPU_screen;
DoDisplay(true);
@ -1718,7 +1718,7 @@ class WinDriver : public BaseDriver
// in multi-core mode now the display thread will probably
// wait for an invocation in this thread to happen,
// so handle that ASAP
if(!CommonSettings.single_core)
if(!CommonSettings.single_core())
{
ResetEvent(display_invoke_ready_event);
SetEvent(display_wakeup_event);
@ -1844,11 +1844,7 @@ int _main()
//this helps give a substantial speedup for singlecore users
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
if(systemInfo.dwNumberOfProcessors==1)
CommonSettings.single_core = true;
else
CommonSettings.single_core = false;
CommonSettings.num_cores = systemInfo.dwNumberOfProcessors;
char text[80];
@ -1948,7 +1944,7 @@ int _main()
//in case this isnt actually a singlecore system, but the user requested it
//then restrict ourselves to one core
if(CommonSettings.single_core)
if(CommonSettings.single_core())
SetProcessAffinityMask(GetCurrentProcess(),1);
MainWindow = new WINCLASS(CLASSNAME, hAppInst);
@ -2130,7 +2126,9 @@ int _main()
hKeyInputTimer = timeSetEvent (KeyInRepeatMSec, 0, KeyInputTimer, 0, TIME_PERIODIC);
cur3DCore = GetPrivateProfileInt("3D", "Renderer", GPU3D_OPENGL, IniName);
CommonSettings.HighResolutionInterpolateColor = GetPrivateProfileBool("3D", "HighResolutionInterpolateColor", 1, IniName);
CommonSettings.GFX3D_HighResolutionInterpolateColor = GetPrivateProfileBool("3D", "HighResolutionInterpolateColor", 1, IniName);
CommonSettings.GFX3D_EdgeMark = GetPrivateProfileBool("3D", "EnableEdgeMark", 1, IniName);
CommonSettings.GFX3D_Fog = GetPrivateProfileBool("3D", "EnableFog", 1, IniName);
//CommonSettings.gfx3d_flushMode = GetPrivateProfileInt("3D", "AlternateFlush", 0, IniName);
NDS_3D_ChangeCore(cur3DCore);
@ -3379,7 +3377,7 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM
}
else
{
if(CommonSettings.single_core)
if(CommonSettings.single_core())
{
video.srcBuffer = (u8*)GPU_screen;
DoDisplay(true);
@ -4406,7 +4404,9 @@ LRESULT CALLBACK GFX3DSettingsDlgProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
int i;
CheckDlgButton(hw,IDC_INTERPOLATECOLOR,CommonSettings.HighResolutionInterpolateColor?1:0);
CheckDlgButton(hw,IDC_INTERPOLATECOLOR,CommonSettings.GFX3D_HighResolutionInterpolateColor?1:0);
CheckDlgButton(hw,IDC_3DSETTINGS_EDGEMARK,CommonSettings.GFX3D_EdgeMark?1:0);
CheckDlgButton(hw,IDC_3DSETTINGS_FOG,CommonSettings.GFX3D_Fog?1:0);
//CheckDlgButton(hw,IDC_ALTERNATEFLUSH,CommonSettings.gfx3d_flushMode);
for(i = 0; core3DList[i] != NULL; i++)
@ -4423,10 +4423,14 @@ LRESULT CALLBACK GFX3DSettingsDlgProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
case IDOK:
{
CommonSettings.HighResolutionInterpolateColor = IsDlgCheckboxChecked(hw,IDC_INTERPOLATECOLOR);
CommonSettings.GFX3D_HighResolutionInterpolateColor = IsDlgCheckboxChecked(hw,IDC_INTERPOLATECOLOR);
CommonSettings.GFX3D_EdgeMark = IsDlgCheckboxChecked(hw,IDC_3DSETTINGS_EDGEMARK);
CommonSettings.GFX3D_Fog = IsDlgCheckboxChecked(hw,IDC_3DSETTINGS_FOG);
NDS_3D_ChangeCore(ComboBox_GetCurSel(GetDlgItem(hw, IDC_3DCORE)));
WritePrivateProfileInt("3D", "Renderer", cur3DCore, IniName);
WritePrivateProfileInt("3D", "HighResolutionInterpolateColor", CommonSettings.HighResolutionInterpolateColor?1:0, IniName);
WritePrivateProfileInt("3D", "HighResolutionInterpolateColor", CommonSettings.GFX3D_HighResolutionInterpolateColor?1:0, IniName);
WritePrivateProfileInt("3D", "EnableEdgeMark", CommonSettings.GFX3D_EdgeMark?1:0, IniName);
WritePrivateProfileInt("3D", "EnableFog", CommonSettings.GFX3D_Fog?1:0, IniName);
//CommonSettings.gfx3d_flushMode = (IsDlgButtonChecked(hw,IDC_ALTERNATEFLUSH) == BST_CHECKED)?1:0;
//WritePrivateProfileInt("3D", "AlternateFlush", CommonSettings.gfx3d_flushMode, IniName);
}

View File

@ -277,7 +277,6 @@
#define IDC_SOUNDCORECB 1000
#define IDC_USEEXTBIOS 1000
#define ID_BROWSE 1000
#define IDC_ALTERNATEFLUSH 1001
#define IDC_BGMAP_BGXCNT 1001
#define IDC_CHECKBOX_DEBUGGERMODE 1001
#define IDC_EDIT01 1001
@ -630,7 +629,9 @@
#define IDC_GI_FATOFS 4464
#define IDC_INTERPOLATECOLOR 4464
#define IDC_GI_FATSIZE 4465
#define IDC_3DSETTINGS_EDGEMARK 4465
#define IDC_GI_ICONTITLEOFS 4466
#define IDC_3DSETTINGS_FOG 4466
#define IDC_GI_USEDROMSIZE 4467
#define IDC_GI_ICON 4469
#define IDC_GI_TITLE 4470

Binary file not shown.