Merged common texture cache code from video plugins into VideoCommon. (DX11 native mipmaps currently broken, disabled) Hopefully everything else should still be working.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6288 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Jordan Woyak 2010-10-19 22:24:27 +00:00
parent 16ff21b0d5
commit cf05cca7a6
25 changed files with 1485 additions and 1922 deletions

View File

@ -95,7 +95,7 @@ void Shutdown()
textureMap.clear();
}
PC_TexFormat GetHiresTex(const char *fileName, int *pWidth, int *pHeight, int texformat, u8 *data)
PC_TexFormat GetHiresTex(const char *fileName, unsigned int *pWidth, unsigned int *pHeight, int texformat, u8 *data)
{
std::string key(fileName);

View File

@ -26,7 +26,7 @@ namespace HiresTextures
{
void Init(const char *gameCode);
void Shutdown();
PC_TexFormat GetHiresTex(const char *fileName, int *pWidth, int *pHeight, int texformat, u8 *data);
PC_TexFormat GetHiresTex(const char *fileName, unsigned int *pWidth, unsigned int *pHeight, int texformat, u8 *data);
};

View File

@ -30,6 +30,7 @@ files = [
'TextureConversionShader.cpp',
'ImageWrite.cpp',
'VertexManagerBase.cpp',
'TextureCacheBase.cpp',
'Statistics.cpp',
'Fifo.cpp',
'VideoState.cpp',

View File

@ -0,0 +1,659 @@
#include "MemoryUtil.h"
#include "VideoConfig.h"
#include "Statistics.h"
#include "HiresTextures.h"
#include "Render.h"
#include "FileUtil.h"
#include "Profiler.h"
#include "PluginSpecs.h"
#include "TextureCacheBase.h"
// ugly
extern int frameCount;
enum
{
TEMP_SIZE = (1024 * 1024 * 4),
TEXTURE_KILL_THRESHOLD = 200,
};
TextureCache *g_texture_cache;
u8 *TextureCache::temp;
TextureCache::TexCache TextureCache::textures;
// returns the exponent of the smallest power of two which is greater than val
unsigned int GetPow2(unsigned int val)
{
unsigned int ret = 0;
for (; val; val >>= 1)
++ret;
return ret;
}
TextureCache::TCacheEntryBase::~TCacheEntryBase()
{
if (0 == addr)
return;
if (!isRenderTarget && !g_ActiveConfig.bSafeTextureCache)
{
u32 *const ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr);
if (ptr && *ptr == hash)
*ptr = oldpixel;
}
}
// TODO: uglyness
extern PLUGIN_GLOBALS *globals;
TextureCache::TextureCache()
{
temp = (u8*)AllocateMemoryPages(TEMP_SIZE);
TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
HiresTextures::Init(globals->unique_id);
}
void TextureCache::Invalidate(bool shutdown)
{
TexCache::iterator
iter = textures.begin(),
tcend = textures.end();
for (; iter != tcend; ++iter)
{
if (shutdown)
iter->second->addr = 0;
delete iter->second;
}
textures.clear();
HiresTextures::Shutdown();
}
TextureCache::~TextureCache()
{
Invalidate(true);
FreeMemoryPages(temp, TEMP_SIZE);
temp = NULL;
}
void TextureCache::Cleanup()
{
TexCache::iterator
iter = textures.begin(),
tcend = textures.end();
while (iter != tcend)
{
if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second->frameCount)
{
delete iter->second;
textures.erase(iter++);
}
else
++iter;
}
}
void TextureCache::InvalidateRange(u32 start_address, u32 size)
{
TexCache::iterator
iter = textures.begin(),
tcend = textures.end();
while (iter != tcend)
{
const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size);
if (0 == rangePosition)
{
delete iter->second;
textures.erase(iter++);
}
else
++iter;
}
}
void TextureCache::MakeRangeDynamic(u32 start_address, u32 size)
{
TexCache::iterator
iter = textures.begin(),
tcend = textures.end();
for (; iter != tcend; ++iter)
{
const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size);
if (0 == rangePosition)
{
iter->second->hash = 0;
}
}
}
int TextureCache::TCacheEntryBase::IntersectsMemoryRange(u32 range_address, u32 range_size) const
{
if (addr + size_in_bytes < range_address)
return -1;
if (addr >= range_address + range_size)
return 1;
return 0;
}
void TextureCache::ClearRenderTargets()
{
TexCache::iterator
iter = textures.begin(),
tcend = textures.end();
for (; iter!=tcend; ++iter)
iter->second->isRenderTarget = false;
}
TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage,
u32 address, unsigned int width, unsigned int height, int texformat,
unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel)
{
// necessary?
if (0 == address)
return NULL;
u8* ptr = g_VideoInitialize.pGetMemoryPointer(address);
// TexelSizeInNibbles(format)*width*height/16;
const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat) - 1;
const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat) - 1;
unsigned int expandedWidth = (width + bsw) & (~bsw);
unsigned int expandedHeight = (height + bsh) & (~bsh);
u64 hash_value = 0;
u64 texHash = 0;
u32 texID = address;
u32 full_format = texformat;
const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat);
const u32 palette_size = TexDecoder_GetPaletteSize(texformat);
bool texture_is_dynamic = false;
// someone who understands this var could rename it :p
const bool isC4_C8_C14X2 = (texformat == GX_TF_C4 || texformat == GX_TF_C8 || texformat == GX_TF_C14X2);
if (isC4_C8_C14X2)
full_format = texformat | (tlutfmt << 16);
// hires texture loading and texture dumping require accurate hashes
if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures)
{
texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples);
if (isC4_C8_C14X2)
{
// WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up)
// tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower.
// This trick (to change the texID depending on the TLUT addr) is a trick to get around
// an issue with metroid prime's fonts, where it has multiple sets of fonts on top of
// each other stored in a single texture, and uses the palette to make different characters
// visible or invisible. Thus, unless we want to recreate the textures for every drawn character,
// we must make sure that texture with different tluts get different IDs.
const u64 tlutHash = GetHash64(texMem + tlutaddr, palette_size,
g_ActiveConfig.iSafeTextureCache_ColorSamples);
texHash ^= tlutHash;
if (g_ActiveConfig.bSafeTextureCache)
texID ^= ((u32)tlutHash) ^ (tlutHash >> 32);
}
if (g_ActiveConfig.bSafeTextureCache)
hash_value = texHash;
}
TCacheEntryBase *entry = textures[texID];
if (entry)
{
if (false == g_ActiveConfig.bSafeTextureCache)
{
if (entry->isRenderTarget || entry->isDynamic)
{
if (false == g_ActiveConfig.bCopyEFBToTexture)
{
hash_value = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples);
if (isC4_C8_C14X2)
{
hash_value ^= GetHash64(&texMem[tlutaddr], palette_size,
g_ActiveConfig.iSafeTextureCache_ColorSamples);
}
}
else
{
hash_value = 0;
}
}
else
{
hash_value = *(u32*)ptr;
}
}
else if ((entry->isRenderTarget || entry->isDynamic) && g_ActiveConfig.bCopyEFBToTexture)
{
hash_value = 0;
}
// TODO: Is the mipLevels check needed?
if (((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr)
|| ((address == entry->addr) && (hash_value == entry->hash) && full_format == entry->format && entry->mipLevels == maxlevel))
{
entry->isDynamic = false;
goto return_entry;
}
else
{
// Let's reload the new texture data into the same texture,
// instead of destroying it and having to create a new one.
// Might speed up movie playback very, very slightly.
texture_is_dynamic = (entry->isRenderTarget || entry->isDynamic) && !g_ActiveConfig.bCopyEFBToTexture;
// TODO: Is the mipLevels check needed?
if (!entry->isRenderTarget &&
((!entry->isDynamic && width == entry->w && height == entry->h && full_format == entry->format && entry->mipLevels == maxlevel)
|| (entry->isDynamic && entry->w == width && entry->h == height)))
{
// reuse the texture
}
else
{
// delete the texture and make a new one
delete entry;
entry = NULL;
}
}
}
const unsigned int nativeW = width;
const unsigned int nativeH = height;
PC_TexFormat pcfmt = PC_TEX_FMT_NONE;
if (g_ActiveConfig.bHiresTextures)
{
// Load Custom textures
char texPathTemp[MAX_PATH];
unsigned int newWidth = width;
unsigned int newHeight = height;
sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, texHash, texformat);
pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, texformat, temp);
if (pcfmt != PC_TEX_FMT_NONE)
{
expandedWidth = width = newWidth;
expandedHeight = height = newHeight;
// TODO: shouldn't generating mips be forced on now?
// native mips with a custom texture wouldn't make sense
}
}
if (pcfmt == PC_TEX_FMT_NONE)
pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth,
expandedHeight, texformat, tlutaddr, tlutfmt, !g_texture_cache->isOGL());
const bool isPow2 = !((width & (width - 1)) || (height & (height - 1)));
unsigned int texLevels = (isPow2 && UseNativeMips && maxlevel) ?
GetPow2(std::max(width, height)) : !isPow2;
if ((texLevels > (maxlevel + 1)) && maxlevel)
texLevels = maxlevel + 1;
// create the entry/texture
if (NULL == entry)
{
textures[texID] = entry = g_texture_cache->CreateTexture(width, height, expandedWidth, texLevels, pcfmt);
entry->addr = address;
entry->format = full_format;
entry->mipLevels = maxlevel;
entry->size_in_bytes = texture_size;
entry->scaledW = entry->w = width;
entry->scaledH = entry->h = height;
entry->nativeH = nativeH;
entry->nativeW = nativeW;
entry->isRenderTarget = false;
entry->isNonPow2 = false;
entry->isDynamic = texture_is_dynamic;
entry->oldpixel = *(u32*)ptr;
if (g_ActiveConfig.bSafeTextureCache || entry->isDynamic)
entry->hash = hash_value;
else
// don't like rand() here
entry->hash = *(u32*)ptr = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF);
}
// load texture
entry->Load(width, height, expandedWidth, 0);
// load mips
if (texLevels > 1 && pcfmt != PC_TEX_FMT_NONE)
{
const unsigned int bsdepth = TexDecoder_GetTexelSizeInNibbles(texformat);
unsigned int level = 1;
unsigned int mipWidth = (width + 1) >> 1;
unsigned int mipHeight = (height + 1) >> 1;
ptr += texture_size;
while ((mipHeight || mipWidth) && (level < texLevels))
{
const unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1;
const unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1;
expandedWidth = (currentWidth + bsw) & (~bsw);
expandedHeight = (currentHeight + bsh) & (~bsh);
TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, !g_texture_cache->isOGL());
entry->Load(currentWidth, currentHeight, expandedWidth, level);
ptr += ((std::max(mipWidth, bsw) * std::max(mipHeight, bsh) * bsdepth) >> 1);
mipWidth >>= 1;
mipHeight >>= 1;
++level;
}
}
// TODO: won't this cause loaded hires textures to be dumped as well?
// dump texture to file
if (g_ActiveConfig.bDumpTextures)
{
char szTemp[MAX_PATH];
char szDir[MAX_PATH];
// make sure that the directory exists
sprintf(szDir, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX), globals->unique_id);
if (false == File::Exists(szDir) || false == File::IsDirectory(szDir))
File::CreateDir(szDir);
sprintf(szTemp, "%s/%s_%08x_%i.png", szDir, globals->unique_id, texHash, texformat);
if (false == File::Exists(szTemp))
entry->Save(szTemp);
}
INCSTAT(stats.numTexturesCreated);
SETSTAT(stats.numTexturesAlive, textures.size());
return_entry:
entry->frameCount = frameCount;
entry->Bind(stage);
return entry;
}
void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer,
bool bIsIntensityFmt, u32 copyfmt, bool bScaleByHalf, const EFBRectangle &source_rect)
{
DVSTARTPROFILE();
float colmat[20] = {};
// last four floats of colmat for fConstAdd
float *const fConstAdd = colmat + 16;
unsigned int cbufid = -1;
if (bFromZBuffer)
{
// TODO: these values differ slightly from the DX9/11 values,
// do they need to? or can this be removed
if (g_texture_cache->isOGL())
{
switch(copyfmt)
{
case 0: // Z4
case 1: // Z8
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
break;
case 3: // Z16 //?
colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
break;
case 11: // Z16 (reverse order)
colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1;
break;
case 6: // Z24X8
colmat[2] = colmat[5] = colmat[8] = colmat[15] = 1;
break;
case 9: // Z8M
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
break;
case 10: // Z8L
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
break;
case 12: // Z16L
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
}
}
else
{
switch (copyfmt)
{
case 0: // Z4
case 1: // Z8
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1.0f;
cbufid = 12;
break;
case 3: // Z16 //?
colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f;
cbufid = 13;
break;
case 11: // Z16 (reverse order)
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1.0f;
cbufid = 14;
break;
case 6: // Z24X8
colmat[0] = colmat[5] = colmat[10] = 1.0f;
cbufid = 15;
break;
case 9: // Z8M
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f;
cbufid = 16;
break;
case 10: // Z8L
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f;
cbufid = 17;
break;
case 12: // Z16L
colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f;
cbufid = 18;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt);
colmat[2] = colmat[5] = colmat[8] = 1.0f;
cbufid = 19;
break;
}
}
}
else if (bIsIntensityFmt)
{
fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f;
switch (copyfmt)
{
case 0: // I4
case 1: // I8
case 2: // IA4
case 3: // IA8
// TODO - verify these coefficients
colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f;
colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f;
colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f;
if (copyfmt < 2)
{
fConstAdd[3] = 16.0f / 255.0f;
colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f;
cbufid = 0;
}
else// alpha
{
colmat[15] = 1;
cbufid = 1;
}
break;
default:
ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
}
}
else
{
switch (copyfmt)
{
case 0: // R4
case 8: // R8
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
cbufid = 2;
break;
case 2: // RA4
case 3: // RA8
colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1;
cbufid = 3;
break;
case 7: // A8
colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1;
cbufid = 4;
break;
case 9: // G8
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
cbufid = 5;
break;
case 10: // B8
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
cbufid = 6;
break;
case 11: // RG8
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
cbufid = 7;
break;
case 12: // GB8
colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
cbufid = 8;
break;
case 4: // RGB565
colmat[0] = colmat[5] = colmat[10] = 1;
fConstAdd[3] = 1; // set alpha to 1
cbufid = 9;
break;
case 5: // RGB5A3
case 6: // RGBA8
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
cbufid = 10;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
cbufid = 11;
break;
}
}
const unsigned int tex_w = (abs(source_rect.GetWidth()) >> (int)bScaleByHalf);
const unsigned int tex_h = (abs(source_rect.GetHeight()) >> (int)bScaleByHalf);
const float xScale = Renderer::GetTargetScaleX();
const float yScale = Renderer::GetTargetScaleY();
unsigned int scaled_tex_w = g_ActiveConfig.bCopyEFBScaled ? (int)(tex_w * xScale) : tex_w;
unsigned int scaled_tex_h = g_ActiveConfig.bCopyEFBScaled ? (int)(tex_h * yScale) : tex_h;
bool texture_is_dynamic = false;
TCacheEntryBase *entry = textures[address];
if (entry)
{
if ((entry->isRenderTarget && entry->scaledW == scaled_tex_w && entry->scaledH == scaled_tex_h)
|| (entry->isDynamic && entry->w == tex_w && entry->h == tex_h))
{
texture_is_dynamic = entry->isDynamic;
}
else
{
// remove it and recreate it as a render target
delete entry;
entry = NULL;
}
}
if (texture_is_dynamic)
{
scaled_tex_w = tex_w;
scaled_tex_h = tex_h;
}
if (NULL == entry)
{
// create the texture
textures[address] = entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h);
entry->addr = address;
entry->hash = 0;
entry->w = entry->nativeW = tex_w;
entry->h = entry->nativeH = tex_h;
entry->scaledW = scaled_tex_w;
entry->scaledH = scaled_tex_h;
entry->format = copyfmt;
entry->mipLevels = 0;
entry->isRenderTarget = true;
entry->isNonPow2 = true;
entry->isDynamic = false;
}
entry->frameCount = frameCount;
Renderer::ResetAPIState(); // reset any game specific settings
entry->FromRenderTarget(bFromZBuffer, bScaleByHalf, cbufid, colmat, source_rect, bIsIntensityFmt, copyfmt);
Renderer::RestoreAPIState();
}

View File

@ -0,0 +1,99 @@
#ifndef _TEXTURECACHEBASE_H
#define _TEXTURECACHEBASE_H
#include <map>
//#include "VideoCommon.h"
#include "TextureDecoder.h"
#include "BPMemory.h"
#include "CommonTypes.h"
class TextureCache
{
public:
struct TCacheEntryBase
{
// TODO: organize
u32 addr;
u32 size_in_bytes;
u64 hash;
//u32 paletteHash;
u32 oldpixel;
u32 format;
int frameCount;
unsigned int w, h, mipLevels;
// TODO: it looks like scaledW/H can be removed and w/h can be used in their place
unsigned int scaledW, scaledH, nativeW, nativeH;
bool isRenderTarget;
bool isDynamic; // mofified from cpu
bool isNonPow2; // doesn't seem to be used anywhere
//TCacheEntryBase()
//{
// // TODO: remove these
// isRenderTarget = 0;
// hash = 0;
// //paletteHash = 0;
// oldpixel = 0;
// addr = 0;
// size_in_bytes = 0;
// frameCount = 0;
// isNonPow2 = true;
// w = 0;
// h = 0;
// scaledW = 0;
// scaledH = 0;
//}
virtual ~TCacheEntryBase();
virtual void Bind(unsigned int stage) = 0;
virtual bool Save(const char filename[]) = 0;
virtual void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level) = 0;
virtual void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf,
unsigned int cbufid, const float *colmat, const EFBRectangle &source_rect,
bool bIsIntensityFmt, u32 copyfmt) = 0;
int IntersectsMemoryRange(u32 range_address, u32 range_size) const;
};
virtual ~TextureCache(); // needs virtual for DX11 dtor
static void Cleanup();
static void Invalidate(bool shutdown);
static void InvalidateRange(u32 start_address, u32 size);
static void MakeRangeDynamic(u32 start_address, u32 size);
static void ClearRenderTargets(); // currently only used by OGL
virtual TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) = 0;
virtual TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) = 0;
static TCacheEntryBase* Load(unsigned int stage, u32 address, unsigned int width, unsigned int height,
int format, unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel);
static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt,
u32 copyfmt, bool bScaleByHalf, const EFBRectangle &source_rect);
protected:
TextureCache();
static u8 *temp;
private:
typedef std::map<u32, TCacheEntryBase*> TexCache;
static TexCache textures;
virtual bool isOGL() { return false; } // Hacks for TextureDecode_real support
};
extern TextureCache *g_texture_cache;
#endif

View File

@ -4,6 +4,12 @@
#include "Statistics.h"
#include "OpcodeDecoding.h"
#include "IndexGenerator.h"
#include "VertexShaderManager.h"
#include "PixelShaderManager.h"
#include "NativeVertexFormat.h"
#include "TextureCacheBase.h"
#include "Render.h"
#include "Profiler.h"
#include "VertexManagerBase.h"
@ -151,8 +157,158 @@ void VertexManager::AddVertices(int primitive, int numVertices)
AddIndices(primitive, numVertices);
}
// TODO: merge this func, (need to merge TextureCache)
void VertexManager::Flush()
{
g_vertex_manager->vFlush();
}
// TODO: need to merge more stuff into VideoCommon to use this
#if (0)
void VertexManager::Flush()
{
if (LocalVBuffer == s_pCurBufferPointer || Flushed)
return;
Flushed = true;
VideoFifo_CheckEFBAccess();
#if defined(_DEBUG) || defined(DEBUGFAST)
PRIM_LOG("frame%d:\n texgen=%d, numchan=%d, dualtex=%d, ztex=%d, cole=%d, alpe=%d, ze=%d", g_ActiveConfig.iSaveTargetId, xfregs.numTexGens,
xfregs.nNumChans, (int)xfregs.bEnableDualTexTransform, bpmem.ztex2.op,
bpmem.blendmode.colorupdate, bpmem.blendmode.alphaupdate, bpmem.zmode.updateenable);
for (int i = 0; i < xfregs.nNumChans; ++i)
{
LitChannel* ch = &xfregs.colChans[i].color;
PRIM_LOG("colchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d", i, ch->matsource, ch->GetFullLightMask(), ch->ambsource, ch->diffusefunc, ch->attnfunc);
ch = &xfregs.colChans[i].alpha;
PRIM_LOG("alpchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d", i, ch->matsource, ch->GetFullLightMask(), ch->ambsource, ch->diffusefunc, ch->attnfunc);
}
for (int i = 0; i < xfregs.numTexGens; ++i)
{
TexMtxInfo tinfo = xfregs.texcoords[i].texmtxinfo;
if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP) tinfo.hex &= 0x7ff;
if (tinfo.texgentype != XF_TEXGEN_REGULAR) tinfo.projection = 0;
PRIM_LOG("txgen%d: proj=%d, input=%d, gentype=%d, srcrow=%d, embsrc=%d, emblght=%d, postmtx=%d, postnorm=%d",
i, tinfo.projection, tinfo.inputform, tinfo.texgentype, tinfo.sourcerow, tinfo.embosssourceshift, tinfo.embosslightshift,
xfregs.texcoords[i].postmtxinfo.index, xfregs.texcoords[i].postmtxinfo.normalize);
}
PRIM_LOG("pixel: tev=%d, ind=%d, texgen=%d, dstalpha=%d, alphafunc=0x%x", bpmem.genMode.numtevstages+1, bpmem.genMode.numindstages,
bpmem.genMode.numtexgens, (u32)bpmem.dstalpha.enable, (bpmem.alphaFunc.hex>>16)&0xff);
#endif
DVSTARTPROFILE();
// set the textures
DVSTARTSUBPROFILE("VertexManager::Flush:textures");
u32 usedtextures = 0;
for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i)
if (bpmem.tevorders[i / 2].getEnable(i & 1))
usedtextures |= 1 << bpmem.tevorders[i/2].getTexMap(i & 1);
if (bpmem.genMode.numindstages > 0)
for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i)
if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages)
usedtextures |= 1 << bpmem.tevindref.getTexMap(bpmem.tevind[i].bt);
for (u32 i = 0; i < 8; ++i)
{
if (usedtextures & (1 << i))
{
// TODO:
//glActiveTexture(GL_TEXTURE0 + i);
Renderer::SetSamplerState(i & 3, i >> 2);
FourTexUnits &tex = bpmem.tex[i >> 2];
TCacheEntry::TCacheEntryBase* tentry = TextureCache::Load(i,
(tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5,
tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1,
tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9,
tex.texTlut[i&3].tlut_format,
(tex.texMode0[i&3].min_filter & 3) && (tex.texMode0[i&3].min_filter != 8) && g_ActiveConfig.bUseNativeMips,
(tex.texMode1[i&3].max_lod >> 4));
if (tentry)
{
// 0s are probably for no manual wrapping needed.
PixelShaderManager::SetTexDims(i, tentry->nativeW, tentry->nativeH, 0, 0);
// TODO:
//if (g_ActiveConfig.iLog & CONF_SAVETEXTURES)
//{
// // save the textures
// char strfile[255];
// sprintf(strfile, "%stex%.3d_%d.tga", File::GetUserPath(D_DUMPFRAMES_IDX), g_Config.iSaveTargetId, i);
// SaveTexture(strfile, GL_TEXTURE_2D, tentry->texture, tentry->w, tentry->h);
//}
}
else
ERROR_LOG(VIDEO, "error loading texture");
}
}
// set global constants
VertexShaderManager::SetConstants();
PixelShaderManager::SetConstants();
// finally bind
if (false == PixelShaderCache::SetShader(false, g_nativeVertexFmt->m_components))
goto shader_fail;
if (false == VertexShaderCache::SetShader(g_nativeVertexFmt->m_components))
goto shader_fail;
const int stride = g_nativeVertexFmt->GetVertexStride();
//if (g_nativeVertexFmt)
g_nativeVertexFmt->SetupVertexPointers();
g_vertex_manager->Draw(stride, false);
// run through vertex groups again to set alpha
if (false == g_ActiveConfig.bDstAlphaPass && bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate)
{
if (false == PixelShaderCache::SetShader(true, g_nativeVertexFmt->m_components))
goto shader_fail;
g_vertex_manager->Draw(stride, true);
}
// TODO:
//IndexGenerator::Start(TIBuffer, LIBuffer, PIBuffer);
#if defined(_DEBUG) || defined(DEBUGFAST)
if (g_ActiveConfig.iLog & CONF_SAVESHADERS)
{
// save the shaders
char strfile[255];
sprintf(strfile, "%sps%.3d.txt", File::GetUserPath(D_DUMPFRAMES_IDX), g_ActiveConfig.iSaveTargetId);
std::ofstream fps(strfile);
fps << ps->strprog.c_str();
sprintf(strfile, "%svs%.3d.txt", File::GetUserPath(D_DUMPFRAMES_IDX), g_ActiveConfig.iSaveTargetId);
std::ofstream fvs(strfile);
fvs << vs->strprog.c_str();
}
if (g_ActiveConfig.iLog & CONF_SAVETARGETS)
{
char str[128];
sprintf(str, "%starg%.3d.tga", File::GetUserPath(D_DUMPFRAMES_IDX), g_ActiveConfig.iSaveTargetId);
TargetRectangle tr;
tr.left = 0;
tr.right = Renderer::GetTargetWidth();
tr.top = 0;
tr.bottom = Renderer::GetTargetHeight();
Renderer::SaveRenderTarget(str, tr);
}
#endif
++g_Config.iSaveTargetId;
shader_fail:
ResetBuffer();
}
#endif

View File

@ -48,8 +48,10 @@ protected:
private:
static void AddIndices(int primitive, int numVertices);
// temporary
//virtual void Draw(u32 stride, bool alphapass) = 0;
// temp
virtual void vFlush() = 0;
};
extern VertexManager *g_vertex_manager;

View File

@ -730,6 +730,14 @@
<Filter
Name="Base"
>
<File
RelativePath=".\Src\TextureCacheBase.cpp"
>
</File>
<File
RelativePath=".\Src\TextureCacheBase.h"
>
</File>
<File
RelativePath=".\Src\VertexManagerBase.cpp"
>

View File

@ -80,7 +80,7 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const
// bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format)
if (!g_ActiveConfig.bEFBCopyDisable)
{
TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, !!scaleByHalf, rc);
}
}

View File

@ -40,39 +40,143 @@
#include "TextureCache.h"
#include "HiresTextures.h"
namespace DX11
{
ID3D11BlendState* efbcopyblendstate = NULL;
ID3D11RasterizerState* efbcopyraststate = NULL;
ID3D11DepthStencilState* efbcopydepthstate = NULL;
ID3D11Buffer* efbcopycbuf[20] = { NULL };
ID3D11Buffer* efbcopycbuf[20] = {};
u8* TextureCache::temp = NULL;
TextureCache::TexCache TextureCache::textures;
extern int frameCount;
#define TEMP_SIZE (2048*2048*4)
#define TEXTURE_KILL_THRESHOLD 200
void TextureCache::TCacheEntry::Destroy(bool shutdown)
TextureCache::TCacheEntry::~TCacheEntry()
{
SAFE_RELEASE(texture);
if (!isRenderTarget && !shutdown && !g_ActiveConfig.bSafeTextureCache)
{
u32 *ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr);
if (ptr && *ptr == hash)
*ptr = oldpixel;
}
texture->Release();
}
void TextureCache::Init()
void TextureCache::TCacheEntry::Bind(unsigned int stage)
{
D3D::gfxstate->SetShaderResource(stage, texture->GetSRV());
}
bool TextureCache::TCacheEntry::Save(const char filename[])
{
return SUCCEEDED(PD3DX11SaveTextureToFileA(D3D::context, texture->GetTex(), D3DX11_IFF_PNG, filename));
}
void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level)
{
// TODO: remove hax
if (level != 0) return;
D3D::ReplaceRGBATexture2D(texture->GetTex(), TextureCache::temp, width, height, expanded_width, level, usage);
}
TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width,
unsigned int height, unsigned int expanded_width,
unsigned int tex_levels, PC_TexFormat pcfmt)
{
D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
D3D11_CPU_ACCESS_FLAG cpu_access = (D3D11_CPU_ACCESS_FLAG)0;
D3D11_SUBRESOURCE_DATA srdata, *data = NULL;
// TODO: remove hax
tex_levels = 1;
if (1 == tex_levels)
{
usage = D3D11_USAGE_DYNAMIC;
cpu_access = D3D11_CPU_ACCESS_WRITE;
srdata.pSysMem = TextureCache::temp;
srdata.SysMemPitch = 4 * expanded_width;
// testing
//data = &srdata;
}
const D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM,
width, height, 1, tex_levels, D3D11_BIND_SHADER_RESOURCE, usage, cpu_access);
ID3D11Texture2D *pTexture;
const HRESULT hr = D3D::device->CreateTexture2D(&texdesc, data, &pTexture);
CHECK(SUCCEEDED(hr), "Create texture of the TextureCache");
TCacheEntry* const entry = new TCacheEntry(new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE));
entry->usage = usage;
// TODO: better debug names
D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetTex(), "a texture of the TextureCache");
D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetSRV(), "shader resource view of a texture of the TextureCache");
SAFE_RELEASE(pTexture);
if (0 == tex_levels)
PD3DX11FilterTexture(D3D::context, entry->texture->GetTex(), 0, D3DX11_DEFAULT);
return entry;
}
void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf,
unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect,
bool bIsIntensityFmt, u32 copyfmt)
{
// stretch picture with increased internal resolution
const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)scaledW, (float)scaledH);
D3D::context->RSSetViewports(1, &vp);
// set transformation
if (NULL == efbcopycbuf[cbufid])
{
const D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(20 * sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT);
D3D11_SUBRESOURCE_DATA data;
data.pSysMem = colmat;
HRESULT hr = D3D::device->CreateBuffer(&cbdesc, &data, &efbcopycbuf[cbufid]);
CHECK(SUCCEEDED(hr), "Create efb copy constant buffer %d", cbufid);
D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopycbuf[cbufid], "a constant buffer used in TextureCache::CopyRenderTargetToTexture");
}
D3D::context->PSSetConstantBuffers(0, 1, &efbcopycbuf[cbufid]);
const TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect);
// TODO: try targetSource.asRECT();
const D3D11_RECT sourcerect = CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom);
// Use linear filtering if (bScaleByHalf), use point filtering otherwise
if (bScaleByHalf)
D3D::SetLinearCopySampler();
else
D3D::SetPointCopySampler();
D3D::stateman->PushBlendState(efbcopyblendstate);
D3D::stateman->PushRasterizerState(efbcopyraststate);
D3D::stateman->PushDepthState(efbcopydepthstate);
D3D::context->OMSetRenderTargets(1, &texture->GetRTV(), NULL);
D3D::drawShadedTexQuad(
(bFromZBuffer) ? g_framebufferManager.GetEFBDepthTexture()->GetSRV() : g_framebufferManager.GetEFBColorTexture()->GetSRV(),
&sourcerect, Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(),
(bFromZBuffer) ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram(),
VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout());
D3D::context->OMSetRenderTargets(1, &g_framebufferManager.GetEFBColorTexture()->GetRTV(), g_framebufferManager.GetEFBDepthTexture()->GetDSV());
D3D::stateman->PopBlendState();
D3D::stateman->PopDepthState();
D3D::stateman->PopRasterizerState();
}
TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
unsigned int scaled_tex_w, unsigned int scaled_tex_h)
{
return new TCacheEntry(D3DTexture2D::Create(scaled_tex_w, scaled_tex_h,
(D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET | (int)D3D11_BIND_SHADER_RESOURCE),
D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM));
}
TextureCache::TextureCache()
{
HRESULT hr;
temp = (u8*)AllocateMemoryPages(TEMP_SIZE);
TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
HiresTextures::Init(globals->unique_id);
D3D11_BLEND_DESC blenddesc;
blenddesc.AlphaToCoverageEnable = FALSE;
blenddesc.IndependentBlendEnable = FALSE;
@ -85,8 +189,8 @@ void TextureCache::Init()
blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
hr = D3D::device->CreateBlendState(&blenddesc, &efbcopyblendstate);
CHECK(hr==S_OK, "Create blend state for TextureCache::CopyRenderTargetToTexture");
D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyblendstate, "blend state used in TextureCache::CopyRenderTargetToTexture");
CHECK(hr==S_OK, "Create blend state for CopyRenderTargetToTexture");
D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyblendstate, "blend state used in CopyRenderTargetToTexture");
D3D11_DEPTH_STENCIL_DESC depthdesc;
depthdesc.DepthEnable = FALSE;
@ -96,8 +200,8 @@ void TextureCache::Init()
depthdesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
depthdesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
hr = D3D::device->CreateDepthStencilState(&depthdesc, &efbcopydepthstate);
CHECK(hr==S_OK, "Create depth state for TextureCache::CopyRenderTargetToTexture");
D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopydepthstate, "depth stencil state used in TextureCache::CopyRenderTargetToTexture");
CHECK(hr==S_OK, "Create depth state for CopyRenderTargetToTexture");
D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopydepthstate, "depth stencil state used in CopyRenderTargetToTexture");
D3D11_RASTERIZER_DESC rastdesc;
rastdesc.CullMode = D3D11_CULL_NONE;
@ -111,528 +215,18 @@ void TextureCache::Init()
rastdesc.MultisampleEnable = false;
rastdesc.AntialiasedLineEnable = false;
hr = D3D::device->CreateRasterizerState(&rastdesc, &efbcopyraststate);
CHECK(hr==S_OK, "Create rasterizer state for TextureCache::CopyRenderTargetToTexture");
D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyraststate, "rasterizer state used in TextureCache::CopyRenderTargetToTexture");
CHECK(hr==S_OK, "Create rasterizer state for CopyRenderTargetToTexture");
D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyraststate, "rasterizer state used in CopyRenderTargetToTexture");
}
void TextureCache::Invalidate(bool shutdown)
TextureCache::~TextureCache()
{
for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter)
iter->second.Destroy(shutdown);
textures.clear();
HiresTextures::Shutdown();
}
void TextureCache::Shutdown()
{
Invalidate(true);
FreeMemoryPages(temp, TEMP_SIZE);
temp = NULL;
SAFE_RELEASE(efbcopyblendstate);
SAFE_RELEASE(efbcopyraststate);
SAFE_RELEASE(efbcopydepthstate);
for (unsigned int k = 0; k < 20;k++)
for (unsigned int k = 0; k < 20; ++k)
SAFE_RELEASE(efbcopycbuf[k]);
}
void TextureCache::Cleanup()
{
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
{
if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount)
{
iter->second.Destroy(false);
iter = textures.erase(iter);
}
else
{
++iter;
}
}
}
void TextureCache::InvalidateRange(u32 start_address, u32 size)
{
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
{
if (iter->second.IntersectsMemoryRange(start_address, size))
{
iter->second.Destroy(false);
textures.erase(iter++);
}
else
{
++iter;
}
}
}
bool TextureCache::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size)
{
if (addr + size_in_bytes < range_address) return false;
if (addr >= range_address + range_size) return false;
return true;
}
// returns the exponent of the smallest power of two which is greater than val
unsigned int GetPow2(unsigned int val)
{
unsigned int ret = 0;
for (;val;val>>=1) ret++;
return ret;
}
TextureCache::TCacheEntry* TextureCache::Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, unsigned int tex_format, unsigned int tlutaddr, unsigned int tlutfmt, bool UseNativeMips, unsigned int maxlevel)
{
// notes (about "UNsafe texture cache"):
// Have to be removed soon.
// But we keep it until the "safe" way became rock solid
// pros: it has an unique ID held by the texture data itself (@address) once cached.
// cons: it writes this unique ID in the gc RAM <- very dangerous (break MP1) and ugly
// notes (about "safe texture cache"):
// Metroids text issue (character table):
// Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs).
// That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2).
// And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but
// have to be a real unique ID.
// DONE but not satifiying yet -> may break copyEFBToTexture sometimes.
// Pokemon Colosseum text issue (plain text):
// Use a GX_TF_I4 512x512 text-flush-texture at a const address.
// The problem here was just the sparse hash on the texture. This texture is partly overwrited (what is needed only)
// so lot's of remaning old text. Thin white chars on black bg too.
// TODO: - clean this up when ready to kill old "unsafe texture cache"
// - fix the key index situation with CopyRenderTargetToTexture.
// Could happen only for GX_TF_C4, GX_TF_C8 and GX_TF_C14X2 fmt.
// Wonder if we can't use tex width&height to know if EFB might be copied to it...
// raw idea: TOCHECK if addresses are aligned we have few bits left...
if (address == 0)
return NULL;
u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address);
unsigned int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16;
unsigned int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16;
unsigned int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format);
unsigned int expandedWidth = (width + bsw) & (~bsw);
unsigned int expandedHeight = (height + bsh) & (~bsh);
u64 hash_value = 0;
u32 texID = address;
u64 texHash = 0;
u32 FullFormat = tex_format;
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
FullFormat = (tex_format | (tlutfmt << 16));
// hires texture loading and texture dumping require accurate hashes
if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures)
{
texHash = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
{
// WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up)
// tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower.
// This trick (to change the texID depending on the TLUT addr) is a trick to get around
// an issue with metroid prime's fonts, where it has multiple sets of fonts on top of
// each other stored in a single texture, and uses the palette to make different characters
// visible or invisible. Thus, unless we want to recreate the textures for every drawn character,
// we must make sure that texture with different tluts get different IDs.
u64 tlutHash = GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
texHash ^= tlutHash;
if (g_ActiveConfig.bSafeTextureCache)
{
texID = texID ^ ((u32)(tlutHash & 0xFFFFFFFF)) ^ ((u32)((tlutHash >> 32) & 0xFFFFFFFF));
}
}
if (g_ActiveConfig.bSafeTextureCache)
hash_value = texHash;
}
bool skip_texture_create = false;
TexCache::iterator iter = textures.find(texID);
if (iter != textures.end())
{
TCacheEntry &entry = iter->second;
if (!g_ActiveConfig.bSafeTextureCache)
hash_value = ((u32*)ptr)[0];
// TODO: Is the (entry.MipLevels == maxlevel) check needed?
if (entry.isRenderTarget || ((address == entry.addr) && (hash_value == entry.hash) && FullFormat == entry.fmt && entry.MipLevels == maxlevel))
{
entry.frameCount = frameCount;
D3D::gfxstate->SetShaderResource(stage, entry.texture->GetSRV());
return &entry;
}
else
{
// Let's reload the new texture data into the same texture,
// instead of destroying it and having to create a new one.
// Might speed up movie playback very, very slightly.
// TODO: Is the (entry.MipLevels < maxlevel) check needed?
if (width == entry.w && height==entry.h && FullFormat == entry.fmt && entry.MipLevels < maxlevel)
{
skip_texture_create = true;
}
else
{
entry.Destroy(false);
textures.erase(iter);
}
}
}
// Make an entry in the table
TCacheEntry& entry = textures[texID];
entry.Realw = width;
entry.Realh = height;
PC_TexFormat pcfmt = PC_TEX_FMT_NONE;
pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt, true);
if (g_ActiveConfig.bHiresTextures)
{
// Load Custom textures
char texPathTemp[MAX_PATH];
int newWidth = width;
int newHeight = height;
sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, texHash, tex_format);
pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, GX_TF_RGBA8, temp);
if (pcfmt != PC_TEX_FMT_NONE)
{
expandedWidth = width = newWidth;
expandedHeight = height = newHeight;
}
}
if (pcfmt == PC_TEX_FMT_NONE)
pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt, true);
entry.oldpixel = ((u32*)ptr)[0];
if (g_ActiveConfig.bSafeTextureCache) entry.hash = hash_value;
else entry.hash = ((u32*)ptr)[0] = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF);
bool isPow2 = !((width & (width - 1)) || (height & (height - 1)));
unsigned int TexLevels = (isPow2 && UseNativeMips && maxlevel) ? GetPow2(max(width, height)) : ((isPow2)? 0 : 1);
if (TexLevels > (maxlevel + 1) && maxlevel)
TexLevels = maxlevel + 1;
D3D11_USAGE usage = (TexLevels == 1) ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
if (!skip_texture_create)
{
// TODO: A little more verbosity in the debug names would be quite helpful..
D3D11_CPU_ACCESS_FLAG cpu_access = (TexLevels == 1) ? D3D11_CPU_ACCESS_WRITE : (D3D11_CPU_ACCESS_FLAG)0;
ID3D11Texture2D* pTexture = NULL;
HRESULT hr;
D3D11_SUBRESOURCE_DATA data;
data.pSysMem = temp;
data.SysMemPitch = 4*expandedWidth;
D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1, TexLevels, D3D11_BIND_SHADER_RESOURCE, usage, cpu_access);
hr = D3D::device->CreateTexture2D(&texdesc, (TexLevels==1)?&data:NULL, &pTexture);
CHECK(hr==S_OK, "Create texture of the TextureCache");
entry.texture = new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE);
CHECK(entry.texture!=NULL, "Create texture of the TextureCache");
D3D::SetDebugObjectName((ID3D11DeviceChild*)entry.texture->GetTex(), "a texture of the TextureCache");
D3D::SetDebugObjectName((ID3D11DeviceChild*)entry.texture->GetSRV(), "shader resource view of a texture of the TextureCache");
SAFE_RELEASE(pTexture);
// if (TexLevels == 1), we already loaded the data into our texture upon creation
if (TexLevels != 1) D3D::ReplaceRGBATexture2D(entry.texture->GetTex(), temp, width, height, expandedWidth, 0, usage);
}
else
{
D3D::ReplaceRGBATexture2D(entry.texture->GetTex(), temp, width, height, expandedWidth, 0, usage);
}
entry.addr = address;
entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format);
entry.isRenderTarget = false;
entry.isNonPow2 = false;
entry.MipLevels = maxlevel;
if (TexLevels == 0) PD3DX11FilterTexture(D3D::context, entry.texture->GetTex(), 0, D3DX11_DEFAULT);
else if (TexLevels > 1 && pcfmt != PC_TEX_FMT_NONE)
{
unsigned int level = 1;
unsigned int mipWidth = (width + 1) >> 1;
unsigned int mipHeight = (height + 1) >> 1;
ptr += entry.size_in_bytes;
while ((mipHeight || mipWidth) && (level < TexLevels))
{
unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1;
unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1;
expandedWidth = (currentWidth + bsw) & (~bsw);
expandedHeight = (currentHeight + bsh) & (~bsh);
TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt, true);
D3D::ReplaceRGBATexture2D(entry.texture->GetTex(), temp, currentWidth, currentHeight, expandedWidth, level, usage);
u32 size = (max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1;
ptr += size;
mipWidth >>= 1;
mipHeight >>= 1;
level++;
}
}
entry.frameCount = frameCount;
entry.w = width;
entry.h = height;
entry.Scaledw = width;
entry.Scaledh = height;
entry.fmt = FullFormat;
// dump texture to file
if (g_ActiveConfig.bDumpTextures)
{
char szTemp[MAX_PATH];
char szDir[MAX_PATH];
// make sure that the directory exists
sprintf(szDir, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX), globals->unique_id);
if (!File::Exists(szDir) || !File::IsDirectory(szDir))
File::CreateDir(szDir);
sprintf(szTemp, "%s/%s_%08x_%i.png", szDir, globals->unique_id, texHash, tex_format);
if (!File::Exists(szTemp))
if(FAILED(PD3DX11SaveTextureToFileA(D3D::context, entry.texture->GetTex(), D3DX11_IFF_PNG, szTemp))) PanicAlert("!!!");
}
INCSTAT(stats.numTexturesCreated);
SETSTAT(stats.numTexturesAlive, textures.size());
D3D::gfxstate->SetShaderResource(stage, entry.texture->GetSRV());
return &entry;
}
void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, unsigned int bScaleByHalf, const EFBRectangle &source_rect)
{
int efb_w = source_rect.GetWidth();
int efb_h = source_rect.GetHeight();
int tex_w = (abs(source_rect.GetWidth()) >> bScaleByHalf);
int tex_h = (abs(source_rect.GetHeight()) >> bScaleByHalf);
int Scaledtex_w = (g_ActiveConfig.bCopyEFBScaled) ? ((int)(Renderer::GetTargetScaleX() * tex_w)) : tex_w;
int Scaledtex_h = (g_ActiveConfig.bCopyEFBScaled) ? ((int)(Renderer::GetTargetScaleY() * tex_h)) : tex_h;
TexCache::iterator iter;
D3DTexture2D* tex = NULL;
iter = textures.find(address);
if (iter != textures.end())
{
if (iter->second.isRenderTarget && iter->second.Scaledw == Scaledtex_w && iter->second.Scaledh == Scaledtex_h)
{
tex = iter->second.texture;
iter->second.frameCount = frameCount;
}
else
{
// Remove it and recreate it as a render target
SAFE_RELEASE(iter->second.texture);
textures.erase(iter);
}
}
if (!tex)
{
TCacheEntry entry;
entry.isRenderTarget = true;
entry.hash = 0;
entry.frameCount = frameCount;
entry.w = entry.Realw = tex_w;
entry.h = entry.Realh = tex_h;
entry.Scaledw = Scaledtex_w;
entry.Scaledh = Scaledtex_h;
entry.fmt = copyfmt;
entry.isNonPow2 = true;
entry.texture = D3DTexture2D::Create(Scaledtex_w, Scaledtex_h, (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET|(int)D3D11_BIND_SHADER_RESOURCE), D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM);
if (entry.texture == NULL) PanicAlert("CopyRenderTargetToTexture failed to create entry.texture at %s %d\n", __FILE__, __LINE__);
textures[address] = entry;
tex = entry.texture;
}
float colmat[20]= {0.0f}; // last four floats for fConstAdd
unsigned int cbufid = (unsigned int)-1;
// TODO: Move this to TextureCache::Init()
if (bFromZBuffer)
{
switch(copyfmt)
{
case 0: // Z4
case 1: // Z8
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1.0f;
cbufid = 12;
break;
case 3: // Z16 //?
colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f;
cbufid = 13;
break;
case 11: // Z16 (reverse order)
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1.0f;
cbufid = 14;
break;
case 6: // Z24X8
colmat[0] = colmat[5] = colmat[10] = 1.0f;
cbufid = 15;
break;
case 9: // Z8M
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f;
cbufid = 16;
break;
case 10: // Z8L
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f;
cbufid = 17;
break;
case 12: // Z16L
colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f;
cbufid = 18;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt);
colmat[2] = colmat[5] = colmat[8] = 1.0f;
cbufid = 19;
break;
}
}
else if (bIsIntensityFmt)
{
colmat[16] = colmat[17] = colmat[18] = 16.0f/255.0f;
switch (copyfmt)
{
case 0: // I4
case 1: // I8
case 2: // IA4
case 3: // IA8
// TODO - verify these coefficients
colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f;
colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f;
colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f;
if (copyfmt < 2)
{
colmat[19] = 16.0f / 255.0f;
colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f;
cbufid = 0;
}
else// alpha
{
colmat[15] = 1;
cbufid = 1;
}
break;
default:
ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
}
}
else
{
switch (copyfmt)
{
case 0: // R4
case 8: // R8
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
cbufid = 2;
break;
case 2: // RA4
case 3: // RA8
colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1;
cbufid = 3;
break;
case 7: // A8
colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1;
cbufid = 4;
break;
case 9: // G8
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
cbufid = 5;
break;
case 10: // B8
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
cbufid = 6;
break;
case 11: // RG8
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
cbufid = 7;
break;
case 12: // GB8
colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
cbufid = 8;
break;
case 4: // RGB565
colmat[0] = colmat[5] = colmat[10] = 1;
colmat[19] = 1; // set alpha to 1
cbufid = 9;
break;
case 5: // RGB5A3
case 6: // RGBA8
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
cbufid = 10;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
cbufid = 11;
break;
}
}
Renderer::ResetAPIState(); // reset any game specific settings
// stretch picture with increased internal resolution
D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)Scaledtex_w, (float)Scaledtex_h);
D3D::context->RSSetViewports(1, &vp);
D3D11_RECT destrect = CD3D11_RECT(0, 0, Scaledtex_w, Scaledtex_h);
// set transformation
if (efbcopycbuf[cbufid] == NULL)
{
D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(20*sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT);
D3D11_SUBRESOURCE_DATA data;
data.pSysMem = colmat;
HRESULT hr = D3D::device->CreateBuffer(&cbdesc, &data, &efbcopycbuf[cbufid]);
CHECK(hr==S_OK, "Create efb copy constant buffer %d", cbufid);
D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopycbuf[cbufid], "a constant buffer used in TextureCache::CopyRenderTargetToTexture");
}
D3D::context->PSSetConstantBuffers(0, 1, &efbcopycbuf[cbufid]);
TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect);
D3D11_RECT sourcerect = CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom);
// Use linear filtering if (bScaleByHalf), use point filtering otherwise
if (bScaleByHalf) D3D::SetLinearCopySampler();
else D3D::SetPointCopySampler();
D3D::stateman->PushBlendState(efbcopyblendstate);
D3D::stateman->PushRasterizerState(efbcopyraststate);
D3D::stateman->PushDepthState(efbcopydepthstate);
D3D::context->OMSetRenderTargets(1, &tex->GetRTV(), NULL);
D3D::drawShadedTexQuad(
(bFromZBuffer) ? g_framebufferManager.GetEFBDepthTexture()->GetSRV() : g_framebufferManager.GetEFBColorTexture()->GetSRV(),
&sourcerect,
Renderer::GetFullTargetWidth(),
Renderer::GetFullTargetHeight(),
(bFromZBuffer) ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram(), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout());
D3D::context->OMSetRenderTargets(1, &g_framebufferManager.GetEFBColorTexture()->GetRTV(), g_framebufferManager.GetEFBDepthTexture()->GetDSV());
D3D::stateman->PopBlendState();
D3D::stateman->PopDepthState();
D3D::stateman->PopRasterizerState();
Renderer::RestoreAPIState();
}

View File

@ -24,45 +24,42 @@
#include "VideoCommon.h"
#include "BPMemory.h"
class TextureCache
#include "TextureCacheBase.h"
namespace DX11
{
class TextureCache : public ::TextureCache
{
public:
struct TCacheEntry
{
D3DTexture2D* texture;
u32 addr;
u32 size_in_bytes;
u64 hash;
u32 paletteHash;
u32 oldpixel;
int frameCount;
unsigned int w, h, fmt, MipLevels;
int Realw, Realh, Scaledw, Scaledh;
bool isRenderTarget;
bool isNonPow2;
TCacheEntry() : texture(NULL), addr(0), size_in_bytes(0), hash(0), paletteHash(0), oldpixel(0),
frameCount(0), w(0), h(0), fmt(0), MipLevels(0), Realw(0), Realh(0), Scaledw(0), Scaledh(0),
isRenderTarget(false), isNonPow2(true) {}
void Destroy(bool shutdown);
bool IntersectsMemoryRange(u32 range_address, u32 range_size);
};
static void Init();
static void Cleanup();
static void Shutdown();
static void Invalidate(bool shutdown);
static void InvalidateRange(u32 start_address, u32 size);
static TCacheEntry* TextureCache::Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, unsigned int tex_format, unsigned int tlutaddr, unsigned int tlutfmt, bool UseNativeMips, unsigned int maxlevel);
static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, unsigned int bScaleByHalf, const EFBRectangle &source_rect);
TextureCache();
~TextureCache();
private:
typedef std::map<u32,TCacheEntry> TexCache;
struct TCacheEntry : TCacheEntryBase
{
D3DTexture2D *const texture;
static u8* temp;
static TexCache textures;
D3D11_USAGE usage;
TCacheEntry(D3DTexture2D *_tex) : texture(_tex) {}
~TCacheEntry();
void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int levels);
void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf,
unsigned int cbufid, const float* colmat, const EFBRectangle &source_rect,
bool bIsIntensityFmt, u32 copyfmt);
void Bind(unsigned int stage);
bool Save(const char filename[]);
};
TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt);
TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h);
};
}

View File

@ -214,7 +214,7 @@ void VertexManager::vFlush()
{
Renderer::SetSamplerState(i & 3, i >> 2);
FourTexUnits &tex = bpmem.tex[i >> 2];
TextureCache::TCacheEntry* tentry = TextureCache::Load(i,
TextureCache::TCacheEntryBase* tentry = TextureCache::Load(i,
(tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5,
tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1,
tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9,
@ -225,7 +225,7 @@ void VertexManager::vFlush()
if (tentry)
{
// 0s are probably for no manual wrapping needed.
PixelShaderManager::SetTexDims(i, tentry->Realw, tentry->Realh, 0, 0);
PixelShaderManager::SetTexDims(i, tentry->nativeW, tentry->nativeH, 0, 0);
}
else
ERROR_LOG(VIDEO, "error loading texture");

View File

@ -215,7 +215,7 @@ void Video_Prepare()
// internal interfaces
Renderer::Init();
TextureCache::Init();
g_texture_cache = new DX11::TextureCache;
g_vertex_manager = new DX11::VertexManager;
VertexShaderCache::Init();
PixelShaderCache::Init();
@ -258,7 +258,7 @@ void Shutdown()
PixelShaderCache::Shutdown();
VertexShaderCache::Shutdown();
delete g_vertex_manager;
TextureCache::Shutdown();
delete g_texture_cache;
Renderer::Shutdown();
EmuWindow::Close();

View File

@ -85,7 +85,7 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const
// bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format)
if (!g_ActiveConfig.bEFBCopyDisable)
{
TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, !!scaleByHalf, rc);
}
}

View File

@ -43,570 +43,44 @@
#include "debugger/debugger.h"
u8 *TextureCache::temp = NULL;
TextureCache::TexCache TextureCache::textures;
extern int frameCount;
#define TEMP_SIZE (2048*2048*4)
#define TEXTURE_KILL_THRESHOLD 200
void TextureCache::TCacheEntry::Destroy(bool shutdown)
namespace DX9
{
if (texture)
texture->Release();
texture = 0;
if (!isRenderTarget && !shutdown && !g_ActiveConfig.bSafeTextureCache)
{
u32 *ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr);
if (ptr && *ptr == hash)
*ptr = oldpixel;
}
TextureCache::TCacheEntry::~TCacheEntry()
{
texture->Release();
}
void TextureCache::Init()
void TextureCache::TCacheEntry::Bind(unsigned int stage)
{
temp = (u8*)AllocateMemoryPages(TEMP_SIZE);
TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
HiresTextures::Init(globals->unique_id);
D3D::SetTexture(stage, texture);
}
void TextureCache::Invalidate(bool shutdown)
bool TextureCache::TCacheEntry::Save(const char filename[])
{
for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter)
iter->second.Destroy(shutdown);
textures.clear();
HiresTextures::Shutdown();
return SUCCEEDED(PD3DXSaveTextureToFileA(filename, D3DXIFF_PNG, texture, 0));
}
void TextureCache::Shutdown()
void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level)
{
Invalidate(true);
FreeMemoryPages(temp, TEMP_SIZE);
temp = NULL;
D3D::ReplaceTexture2D(texture, temp, width, height, expanded_width, d3d_fmt, swap_r_b, level);
}
void TextureCache::Cleanup()
void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf,
unsigned int cbufid, const float *colmat, const EFBRectangle &source_rect,
bool bIsIntensityFmt, u32 copyfmt)
{
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
const LPDIRECT3DTEXTURE9 read_texture = bFromZBuffer ?
g_framebufferManager.GetEFBDepthTexture(source_rect) :
g_framebufferManager.GetEFBColorTexture(source_rect);
if (!isDynamic || g_ActiveConfig.bCopyEFBToTexture)
{
if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount)
{
iter->second.Destroy(false);
iter = textures.erase(iter);
}
else
{
++iter;
}
}
}
void TextureCache::InvalidateRange(u32 start_address, u32 size)
{
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
{
int rangePosition = iter->second.IntersectsMemoryRange(start_address, size);
if (rangePosition == 0)
{
iter->second.Destroy(false);
textures.erase(iter++);
}
else
{
++iter;
}
}
}
void TextureCache::MakeRangeDynamic(u32 start_address, u32 size)
{
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
{
int rangePosition = iter->second.IntersectsMemoryRange(start_address, size);
if ( rangePosition == 0)
{
iter->second.hash = 0;
}
++iter;
}
}
int TextureCache::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size)
{
if (addr + size_in_bytes < range_address)
return -1;
if (addr >= range_address + range_size)
return 1;
return 0;
}
TextureCache::TCacheEntry *TextureCache::Load(int stage, u32 address, int width, int height, int tex_format, int tlutaddr, int tlutfmt,bool UseNativeMips, int maxlevel)
{
// notes (about "UNsafe texture cache"):
// Have to be removed soon.
// But we keep it until the "safe" way became rock solid
// pros: it has an unique ID held by the texture data itself (@address) once cached.
// cons: it writes this unique ID in the gc RAM <- very dangerous (break MP1) and ugly
// notes (about "safe texture cache"):
// Metroids text issue (character table):
// Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs).
// That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2).
// And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but
// have to be a real unique ID.
// DONE but not satifiying yet -> may break copyEFBToTexture sometimes.
// Pokemon Colosseum text issue (plain text):
// Use a GX_TF_I4 512x512 text-flush-texture at a const address.
// The problem here was just the sparse hash on the texture. This texture is partly overwrited (what is needed only)
// so lot's of remaning old text. Thin white chars on black bg too.
// TODO: - clean this up when ready to kill old "unsafe texture cache"
// - fix the key index situation with CopyRenderTargetToTexture.
// Could happen only for GX_TF_C4, GX_TF_C8 and GX_TF_C14X2 fmt.
// Wonder if we can't use tex width&height to know if EFB might be copied to it...
// raw idea: TOCHECK if addresses are aligned we have few bits left...
if (address == 0)
return NULL;
u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address);
int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16;
int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16;
int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format);
int expandedWidth = (width + bsw) & (~bsw);
int expandedHeight = (height + bsh) & (~bsh);
u64 hash_value = 0;
u32 texID = address;
u64 texHash = 0;
u32 FullFormat = tex_format;
bool TextureisDynamic = false;
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
FullFormat = (tex_format | (tlutfmt << 16));
if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures)
{
texHash = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
{
// WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up)
// tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower.
// This trick (to change the texID depending on the TLUT addr) is a trick to get around
// an issue with metroid prime's fonts, where it has multiple sets of fonts on top of
// each other stored in a single texture, and uses the palette to make different characters
// visible or invisible. Thus, unless we want to recreate the textures for every drawn character,
// we must make sure that texture with different tluts get different IDs.
u64 tlutHash = GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
texHash ^= tlutHash;
if (g_ActiveConfig.bSafeTextureCache)
{
texID = texID ^ ((u32)(tlutHash & 0xFFFFFFFF)) ^ ((u32)((tlutHash >> 32) & 0xFFFFFFFF));
}
}
if (g_ActiveConfig.bSafeTextureCache)
hash_value = texHash;
}
bool skip_texture_create = false;
TexCache::iterator iter = textures.find(texID);
if (iter != textures.end())
{
TCacheEntry &entry = iter->second;
if (!g_ActiveConfig.bSafeTextureCache)
{
if(entry.isRenderTarget || entry.isDynamic)
{
if(!g_ActiveConfig.bCopyEFBToTexture)
{
hash_value = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
{
hash_value ^= GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
}
}
else
{
hash_value = 0;
}
}
else
{
hash_value = ((u32 *)ptr)[0];
}
}
else
{
if(entry.isRenderTarget || entry.isDynamic)
{
if(g_ActiveConfig.bCopyEFBToTexture)
{
hash_value = 0;
}
}
}
if (((entry.isRenderTarget || entry.isDynamic) && hash_value == entry.hash && address == entry.addr)
|| ((address == entry.addr) && (hash_value == entry.hash) && FullFormat == entry.fmt/* && entry.MipLevels == maxlevel*/))
{
entry.frameCount = frameCount;
entry.isDynamic = false;
D3D::SetTexture(stage, entry.texture);
return &entry;
}
else
{
// Let's reload the new texture data into the same texture,
// instead of destroying it and having to create a new one.
// Might speed up movie playback very, very slightly.
TextureisDynamic = (entry.isRenderTarget || entry.isDynamic) && !g_ActiveConfig.bCopyEFBToTexture;
if (!entry.isRenderTarget &&
((!entry.isDynamic && width == entry.w && height==entry.h && FullFormat == entry.fmt /* && entry.MipLevels < maxlevel*/)
|| (entry.isDynamic && entry.w == width && entry.h == height)))
{
skip_texture_create = true;
}
else
{
entry.Destroy(false);
textures.erase(iter);
}
}
}
// Make an entry in the table
TCacheEntry& entry = textures[texID];
entry.isDynamic = TextureisDynamic;
entry.Realw = width;
entry.Realh = height;
PC_TexFormat pcfmt = PC_TEX_FMT_NONE;
if (g_ActiveConfig.bHiresTextures)
{
// Load Custom textures
char texPathTemp[MAX_PATH];
int newWidth = width;
int newHeight = height;
sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, texHash, tex_format);
pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, tex_format, temp);
if (pcfmt != PC_TEX_FMT_NONE)
{
expandedWidth = width = newWidth;
expandedHeight = height = newHeight;
}
}
if (pcfmt == PC_TEX_FMT_NONE)
pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt);
entry.oldpixel = ((u32 *)ptr)[0];
if (g_ActiveConfig.bSafeTextureCache || entry.isDynamic)
entry.hash = hash_value;
else
{
entry.hash = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF);
((u32 *)ptr)[0] = entry.hash;
}
entry.addr = address;
entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format);
entry.isRenderTarget = false;
bool isPow2 = !((width & (width - 1)) || (height & (height - 1)));
entry.isNonPow2 = false;
int TexLevels = (width > height)?width:height;
TexLevels = (isPow2 && UseNativeMips && (maxlevel > 0)) ? (int)(log((double)TexLevels)/log((double)2)) + 1 : (isPow2? 0 : 1);
if(TexLevels > (maxlevel + 1) && maxlevel > 0)
TexLevels = (maxlevel + 1);
entry.MipLevels = maxlevel;
D3DFORMAT d3d_fmt;
bool swap_r_b = false;
switch (pcfmt)
{
case PC_TEX_FMT_BGRA32:
d3d_fmt = D3DFMT_A8R8G8B8;
break;
case PC_TEX_FMT_RGBA32:
d3d_fmt = D3DFMT_A8R8G8B8;
swap_r_b = true;
break;
case PC_TEX_FMT_RGB565:
d3d_fmt = D3DFMT_R5G6B5;
break;
case PC_TEX_FMT_IA4_AS_IA8:
d3d_fmt = D3DFMT_A8L8;
break;
case PC_TEX_FMT_I8:
case PC_TEX_FMT_I4_AS_I8:
// A hack which means the format is a packed
// 8-bit intensity texture. It is unpacked
// to A8L8 in D3DTexture.cpp
d3d_fmt = D3DFMT_A8P8;
break;
case PC_TEX_FMT_IA8:
d3d_fmt = D3DFMT_A8L8;
break;
case PC_TEX_FMT_DXT1:
d3d_fmt = D3DFMT_DXT1;
break;
}
if (!skip_texture_create)
{
entry.texture = D3D::CreateTexture2D((BYTE*)temp, width, height, expandedWidth, d3d_fmt, swap_r_b, TexLevels);
}
else
{
D3D::ReplaceTexture2D(entry.texture, (BYTE*)temp, width, height, expandedWidth, d3d_fmt, swap_r_b, 0);
}
if(TexLevels > 1 && pcfmt != PC_TEX_FMT_NONE)
{
int level = 1;
int mipWidth = (width + 1) >> 1;
int mipHeight = (height + 1) >> 1;
ptr += entry.size_in_bytes;
while((mipHeight || mipWidth) && (level < TexLevels))
{
u32 currentWidth = (mipWidth > 0)? mipWidth : 1;
u32 currentHeight = (mipHeight > 0)? mipHeight : 1;
expandedWidth = (currentWidth + bsw) & (~bsw);
expandedHeight = (currentHeight + bsh) & (~bsh);
TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt);
D3D::ReplaceTexture2D(entry.texture, (BYTE*)temp, currentWidth, currentHeight, expandedWidth, d3d_fmt, swap_r_b,level);
u32 size = (max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1;
ptr += size;
mipWidth >>= 1;
mipHeight >>= 1;
level++;
}
}
entry.frameCount = frameCount;
entry.w = width;
entry.h = height;
entry.Scaledw = width;
entry.Scaledh = height;
entry.fmt = FullFormat;
if (g_ActiveConfig.bDumpTextures)
{
// dump texture to file
char szTemp[MAX_PATH];
char szDir[MAX_PATH];
const char* uniqueId = globals->unique_id;
static bool bCheckedDumpDir = false;
sprintf(szDir, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX), uniqueId);
if (!bCheckedDumpDir)
{
if (!File::Exists(szDir) || !File::IsDirectory(szDir))
File::CreateDir(szDir);
bCheckedDumpDir = true;
}
sprintf(szTemp, "%s/%s_%08x_%i.png", szDir, uniqueId, texHash, tex_format);
if (!File::Exists(szTemp))
PD3DXSaveTextureToFileA(szTemp,D3DXIFF_PNG,entry.texture,0);
}
INCSTAT(stats.numTexturesCreated);
SETSTAT(stats.numTexturesAlive, textures.size());
//Set the texture!
D3D::SetTexture(stage, entry.texture);
DEBUGGER_PAUSE_LOG_AT(NEXT_NEW_TEXTURE,true,{printf("A new texture (%d x %d) is loaded", width, height);});
return &entry;
}
void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source_rect)
{
int tex_w = (abs(source_rect.GetWidth()) >> bScaleByHalf);
int tex_h = (abs(source_rect.GetHeight()) >> bScaleByHalf);
//compensate the texture grow if supersampling is enabled to conserve memory usage
float SuperSampleCompensation = g_ActiveConfig.iMultisampleMode + 1;
SuperSampleCompensation = 1.0f / SuperSampleCompensation;
float xScale = Renderer::GetTargetScaleX();
float yScale = Renderer::GetTargetScaleY();
int Scaledtex_w = (g_ActiveConfig.bCopyEFBScaled)?((int)(xScale * SuperSampleCompensation * tex_w)):tex_w;
int Scaledtex_h = (g_ActiveConfig.bCopyEFBScaled)?((int)(yScale * SuperSampleCompensation * tex_h)):tex_h;
TexCache::iterator iter;
LPDIRECT3DTEXTURE9 tex = NULL;
iter = textures.find(address);
bool TextureisDynamic = false;
if (iter != textures.end())
{
if ((iter->second.isRenderTarget && iter->second.Scaledw == Scaledtex_w && iter->second.Scaledh == Scaledtex_h)
|| (iter->second.isDynamic && iter->second.w == tex_w && iter->second.h == tex_h))
{
tex = iter->second.texture;
TextureisDynamic = iter->second.isDynamic;
iter->second.frameCount = frameCount;
}
else
{
// Remove it and recreate it as a render target
if(iter->second.texture)
iter->second.texture->Release();
iter->second.texture = 0;
textures.erase(iter);
}
}
if (TextureisDynamic)
{
Scaledtex_w = tex_w;
Scaledtex_h = tex_h;
}
if (!tex)
{
TCacheEntry entry;
entry.addr = address;
entry.isRenderTarget = true;
entry.hash = 0;
entry.frameCount = frameCount;
entry.w = entry.Realw = tex_w;
entry.h = entry.Realh = tex_h;
entry.Scaledw = Scaledtex_w;
entry.Scaledh = Scaledtex_h;
entry.fmt = copyfmt;
entry.isNonPow2 = true;
entry.isDynamic = false;
D3D::dev->CreateTexture(Scaledtex_w, Scaledtex_h, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &entry.texture, 0);
textures[address] = entry;
tex = entry.texture;
}
// Make sure to resolve anything we need to read from.
LPDIRECT3DTEXTURE9 read_texture = bFromZBuffer ? g_framebufferManager.GetEFBDepthTexture(source_rect) : g_framebufferManager.GetEFBColorTexture(source_rect);
// We have to run a pixel shader, for color conversion.
Renderer::ResetAPIState(); // reset any game specific settings
if(!TextureisDynamic || g_ActiveConfig.bCopyEFBToTexture)
{
float colmat[16]= {0.0f};
float fConstAdd[4] = {0.0f};
if (bFromZBuffer)
{
switch(copyfmt)
{
case 0: // Z4
case 1: // Z8
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1.0f;
break;
case 3: // Z16 //?
colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f;
break;
case 11: // Z16 (reverse order)
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1.0f;
break;
case 6: // Z24X8
colmat[0] = colmat[5] = colmat[10] = 1.0f;
break;
case 9: // Z8M
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f;
break;
case 10: // Z8L
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f;
break;
case 12: // Z16L
colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt);
colmat[2] = colmat[5] = colmat[8] = 1.0f;
break;
}
}
else if (bIsIntensityFmt)
{
fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f;
switch (copyfmt)
{
case 0: // I4
case 1: // I8
case 2: // IA4
case 3: // IA8
colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f;
colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f;
colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f;
if (copyfmt < 2)
{
fConstAdd[3] = 16.0f / 255.0f;
colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f;
}
else// alpha
colmat[15] = 1;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
}
}
else
{
switch (copyfmt)
{
case 0: // R4
case 8: // R8
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
break;
case 2: // RA4
case 3: // RA8
colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1;
break;
case 7: // A8
colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1;
break;
case 9: // G8
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
break;
case 10: // B8
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
break;
case 11: // RG8
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
break;
case 12: // GB8
colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
break;
case 4: // RGB565
colmat[0] = colmat[5] = colmat[10] = 1;
fConstAdd[3] = 1; // set alpha to 1
break;
case 5: // RGB5A3
case 6: // RGBA8
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
}
}
LPDIRECT3DSURFACE9 Rendersurf = NULL;
tex->GetSurfaceLevel(0,&Rendersurf);
texture->GetSurfaceLevel(0, &Rendersurf);
D3D::dev->SetDepthStencilSurface(NULL);
D3D::dev->SetRenderTarget(0, Rendersurf);
@ -615,18 +89,18 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo
// Stretch picture with increased internal resolution
vp.X = 0;
vp.Y = 0;
vp.Width = Scaledtex_w;
vp.Height = Scaledtex_h;
vp.Width = scaledW;
vp.Height = scaledH;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
D3D::dev->SetViewport(&vp);
RECT destrect;
destrect.bottom = Scaledtex_h;
destrect.bottom = scaledH;
destrect.left = 0;
destrect.right = Scaledtex_w;
destrect.right = scaledW;
destrect.top = 0;
const float* const fConstAdd = colmat + 16; // fConstAdd is the last 4 floats of colmat
PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation
TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect);
RECT sourcerect;
@ -635,10 +109,9 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo
sourcerect.right = targetSource.right;
sourcerect.top = targetSource.top;
if(bFromZBuffer)
if (bFromZBuffer)
{
if(bScaleByHalf || g_ActiveConfig.iMultisampleMode)
if (bScaleByHalf || g_ActiveConfig.iMultisampleMode)
{
D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
@ -655,30 +128,29 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo
D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
}
D3DFORMAT bformat = g_framebufferManager.GetEFBDepthRTSurfaceFormat();
int SSAAMode = g_ActiveConfig.iMultisampleMode;
D3D::drawShadedTexQuad(
read_texture,
&sourcerect,
Renderer::GetFullTargetWidth() ,
Renderer::GetFullTargetHeight(),
Scaledtex_w,
Scaledtex_h,
((bformat != FOURCC_RAWZ && bformat != D3DFMT_D24X8) && bFromZBuffer)? PixelShaderCache::GetDepthMatrixProgram(SSAAMode): PixelShaderCache::GetColorMatrixProgram(SSAAMode),
D3D::drawShadedTexQuad(read_texture, &sourcerect,
Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(),
scaledW, scaledH,
((bformat != FOURCC_RAWZ && bformat != D3DFMT_D24X8) && bFromZBuffer) ?
PixelShaderCache::GetDepthMatrixProgram(SSAAMode) :
PixelShaderCache::GetColorMatrixProgram(SSAAMode),
VertexShaderCache::GetSimpleVertexShader(SSAAMode));
Rendersurf->Release();
}
if(!g_ActiveConfig.bCopyEFBToTexture)
if (!g_ActiveConfig.bCopyEFBToTexture)
{
textures[address].hash = TextureConverter::EncodeToRamFromTexture(
address,
hash = TextureConverter::EncodeToRamFromTexture(
addr,
read_texture,
Renderer::GetFullTargetWidth(),
Renderer::GetFullTargetHeight(),
xScale,
yScale,
Renderer::GetTargetScaleX(),
Renderer::GetTargetScaleY(),
(float)((Renderer::GetFullTargetWidth() - Renderer::GetTargetWidth()) / 2),
(float)((Renderer::GetFullTargetHeight() - Renderer::GetTargetHeight()) / 2) ,
bFromZBuffer,
@ -690,8 +162,68 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo
D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER);
D3D::RefreshSamplerState(0, D3DSAMP_MAGFILTER);
D3D::SetTexture(0,NULL);
D3D::SetTexture(0, NULL);
D3D::dev->SetRenderTarget(0, g_framebufferManager.GetEFBColorRTSurface());
D3D::dev->SetDepthStencilSurface(g_framebufferManager.GetEFBDepthRTSurface());
Renderer::RestoreAPIState();
}
TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt)
{
D3DFORMAT d3d_fmt;
bool swap_r_b = false;
switch (pcfmt)
{
case PC_TEX_FMT_BGRA32:
d3d_fmt = D3DFMT_A8R8G8B8;
break;
case PC_TEX_FMT_RGBA32:
d3d_fmt = D3DFMT_A8R8G8B8;
swap_r_b = true;
break;
case PC_TEX_FMT_RGB565:
d3d_fmt = D3DFMT_R5G6B5;
break;
case PC_TEX_FMT_IA4_AS_IA8:
d3d_fmt = D3DFMT_A8L8;
break;
case PC_TEX_FMT_I8:
case PC_TEX_FMT_I4_AS_I8:
// A hack which means the format is a packed
// 8-bit intensity texture. It is unpacked
// to A8L8 in D3DTexture.cpp
d3d_fmt = D3DFMT_A8P8;
break;
case PC_TEX_FMT_IA8:
d3d_fmt = D3DFMT_A8L8;
break;
case PC_TEX_FMT_DXT1:
d3d_fmt = D3DFMT_DXT1;
break;
}
TCacheEntry* entry = new TCacheEntry(D3D::CreateTexture2D(temp, width, height, expanded_width, d3d_fmt, swap_r_b, tex_levels));
entry->swap_r_b = swap_r_b;
entry->d3d_fmt = d3d_fmt;
return entry;
}
TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
unsigned int scaled_tex_w, unsigned int scaled_tex_h)
{
LPDIRECT3DTEXTURE9 texture;
D3D::dev->CreateTexture(scaled_tex_w, scaled_tex_h, 1, D3DUSAGE_RENDERTARGET,
D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture, 0);
return new TCacheEntry(texture);
}
}

View File

@ -25,64 +25,41 @@
#include "VideoCommon.h"
#include "BPMemory.h"
class TextureCache
#include "TextureCacheBase.h"
namespace DX9
{
public:
struct TCacheEntry
class TextureCache : public ::TextureCache
{
private:
struct TCacheEntry : TCacheEntryBase
{
LPDIRECT3DTEXTURE9 texture;
const LPDIRECT3DTEXTURE9 texture;
u32 addr;
u32 size_in_bytes;
u64 hash;
u32 paletteHash;
u32 oldpixel;
D3DFORMAT d3d_fmt;
bool swap_r_b;
int frameCount;
int w, h, fmt,MipLevels, Realw, Realh, Scaledw, Scaledh;
TCacheEntry(LPDIRECT3DTEXTURE9 _tex) : texture(_tex) {}
~TCacheEntry();
bool isRenderTarget;
bool isDynamic;// mofified from cpu
bool isNonPow2;
void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int levels);
TCacheEntry()
{
texture = 0;
isRenderTarget = 0;
hash = 0;
paletteHash = 0;
oldpixel = 0;
addr = 0;
size_in_bytes = 0;
frameCount = 0;
isNonPow2 = true;
w = 0;
h = 0;
Realw = 0;
Realh = 0;
Scaledw = 0;
Scaledh = 0;
}
void Destroy(bool shutdown);
int IntersectsMemoryRange(u32 range_address, u32 range_size);
void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf,
unsigned int cbufid, const float* colmat, const EFBRectangle &source_rect,
bool bIsIntensityFmt, u32 copyfmt);
void Bind(unsigned int stage);
bool Save(const char filename[]);
};
private:
TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt);
typedef std::map<u32,TCacheEntry> TexCache;
static u8 *temp;
static TexCache textures;
public:
static void Init();
static void Cleanup();
static void Shutdown();
static void Invalidate(bool shutdown);
static void InvalidateRange(u32 start_address, u32 size);
static void MakeRangeDynamic(u32 start_address, u32 size);
static TCacheEntry *Load(int stage, u32 address, int width, int height, int format, int tlutaddr, int tlutfmt,bool UseNativeMips, int maxlevel);
static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source_rect);
TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h);
};
}
#endif

View File

@ -133,7 +133,7 @@ void VertexManager::vFlush()
{
Renderer::SetSamplerState(i & 3, i >> 2);
FourTexUnits &tex = bpmem.tex[i >> 2];
TextureCache::TCacheEntry* tentry = TextureCache::Load(i,
TextureCache::TCacheEntryBase* tentry = TextureCache::Load(i,
(tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5,
tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1,
tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9,
@ -144,7 +144,7 @@ void VertexManager::vFlush()
if (tentry)
{
// 0s are probably for no manual wrapping needed.
PixelShaderManager::SetTexDims(i, tentry->Realw, tentry->Realh, 0, 0);
PixelShaderManager::SetTexDims(i, tentry->nativeW, tentry->nativeH, 0, 0);
}
else
ERROR_LOG(VIDEO, "error loading texture");

View File

@ -238,7 +238,7 @@ void Video_Prepare()
// internal interfaces
Renderer::Init();
TextureCache::Init();
g_texture_cache = new DX9::TextureCache;
g_vertex_manager = new DX9::VertexManager;
// VideoCommon
BPInit();
@ -276,7 +276,7 @@ void Shutdown()
PixelShaderCache::Shutdown();
VertexShaderCache::Shutdown();
delete g_vertex_manager;
TextureCache::Shutdown();
delete g_texture_cache;
Renderer::Shutdown();
D3D::Shutdown();
EmuWindow::Close();

View File

@ -83,7 +83,7 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const
// bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format)
if (!g_ActiveConfig.bEFBCopyDisable)
{
TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, !!scaleByHalf, rc);
}
}

View File

@ -1082,7 +1082,7 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons
// Disable all other stages
for (int i = 1; i < 8; ++i)
TextureCache::DisableStage(i);
OGL::TextureCache::DisableStage(i);
// Update GLViewPort
glViewport(dst_rect.left, dst_rect.bottom, dst_rect.GetWidth(), dst_rect.GetHeight());
@ -1246,7 +1246,7 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons
}
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
TextureCache::DisableStage(0);
OGL::TextureCache::DisableStage(0);
// Wireframe
if (g_ActiveConfig.bWireFrame)

View File

@ -53,15 +53,11 @@
#include "VertexShaderManager.h"
#include "VideoConfig.h"
u8 *TextureCache::temp = NULL;
TextureCache::TexCache TextureCache::textures;
namespace OGL
{
extern int frameCount;
static u32 s_TempFramebuffer = 0;
#define TEMP_SIZE (2048*2048*4)
#define TEXTURE_KILL_THRESHOLD 200
static const GLint c_MinLinearFilter[8] = {
GL_NEAREST,
GL_NEAREST_MIPMAP_NEAREST,
@ -83,352 +79,63 @@ static const GLint c_WrapSettings[4] = {
bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int height)
{
std::vector<u32> data(width * height);
glBindTexture(textarget, tex);
glGetTexImage(textarget, 0, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]);
GLenum err = GL_REPORT_ERROR();
if (err != GL_NO_ERROR)
glBindTexture(textarget, tex);
glGetTexImage(textarget, 0, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]);
const GLenum err = GL_REPORT_ERROR();
if (GL_NO_ERROR != err)
{
PanicAlert("Can't save texture, GL Error: %s", gluErrorString(err));
return false;
}
return false;
}
return SaveTGA(filename, width, height, &data[0]);
}
void TextureCache::TCacheEntry::SetTextureParameters(TexMode0 &newmode,TexMode1 &newmode1)
TextureCache::TCacheEntry::~TCacheEntry()
{
mode = newmode;
mode1 = newmode1;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
(newmode.mag_filter || g_Config.bForceFiltering) ? GL_LINEAR : GL_NEAREST);
if (bHaveMipMaps)
if (texture)
{
if (g_ActiveConfig.bForceFiltering && newmode.min_filter < 4)
mode.min_filter += 4; // take equivalent forced linear
int filt = newmode.min_filter;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c_MinLinearFilter[filt & 7]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, newmode1.min_lod >> 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, newmode1.max_lod >> 4);
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, (newmode.lod_bias/32.0f));
}
else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
(g_ActiveConfig.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c_WrapSettings[newmode.wrap_s]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c_WrapSettings[newmode.wrap_t]);
if (g_Config.iMaxAnisotropy >= 1)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)(1 << g_ActiveConfig.iMaxAnisotropy));
}
void TextureCache::TCacheEntry::Destroy(bool shutdown)
{
if (texture)
glDeleteTextures(1, &texture);
texture = 0;
if (!isRenderTarget && !shutdown && !g_ActiveConfig.bSafeTextureCache)
{
u32 *ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr);
if (ptr && *ptr == hash)
*ptr = oldpixel;
texture = 0;
}
}
void TextureCache::Init()
TextureCache::TCacheEntry::TCacheEntry()
{
temp = (u8*)AllocateMemoryPages(TEMP_SIZE);
TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
HiresTextures::Init(globals->unique_id);
}
void TextureCache::Invalidate(bool shutdown)
{
for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter)
iter->second.Destroy(shutdown);
textures.clear();
HiresTextures::Shutdown();
}
void TextureCache::Shutdown()
{
Invalidate(true);
if (s_TempFramebuffer)
{
glDeleteFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer);
s_TempFramebuffer = 0;
}
FreeMemoryPages(temp, TEMP_SIZE);
temp = NULL;
}
void TextureCache::Cleanup()
{
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
{
if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount)
{
iter->second.Destroy(false);
textures.erase(iter++);
}
else
{
++iter;
}
}
}
void TextureCache::InvalidateRange(u32 start_address, u32 size)
{
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
{
int rangePosition = iter->second.IntersectsMemoryRange(start_address, size);
if (rangePosition == 0)
{
iter->second.Destroy(false);
textures.erase(iter++);
}
else
{
++iter;
}
}
}
void TextureCache::MakeRangeDynamic(u32 start_address, u32 size)
{
TexCache::iterator iter = textures.begin();
while (iter != textures.end())
{
int rangePosition = iter->second.IntersectsMemoryRange(start_address, size);
if ( rangePosition == 0)
{
iter->second.hash = 0;
}
++iter;
}
}
int TextureCache::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size)
{
if (addr + size_in_bytes < range_address)
return -1;
if (addr >= range_address + range_size)
return 1;
return 0;
}
TextureCache::TCacheEntry* TextureCache::Load(int texstage, u32 address, int width, int height, u32 tex_format, int tlutaddr, int tlutfmt)
{
// notes (about "UNsafe texture cache"):
// Have to be removed soon.
// But we keep it until the "safe" way became rock solid
// pros: it has an unique ID held by the texture data itself (@address) once cached.
// cons: it writes this unique ID in the gc RAM <- very dangerous (break MP1) and ugly
// notes (about "safe texture cache"):
// Metroids text issue (character table):
// Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs).
// That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2).
// And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but
// have to be a real unique ID.
// DONE but not satifiying yet -> may break copyEFBToTexture sometimes.
// Pokemon Colosseum text issue (plain text):
// Use a GX_TF_I4 512x512 text-flush-texture at a const address.
// The problem here was just the sparse hash on the texture. This texture is partly overwrited (what is needed only)
// so lot's of remaning old text. Thin white chars on black bg too.
// TODO: - clean this up when ready to kill old "unsafe texture cache"
// - fix the key index situation with CopyRenderTargetToTexture.
// Could happen only for GX_TF_C4, GX_TF_C8 and GX_TF_C14X2 fmt.
// Wonder if we can't use tex width&height to know if EFB might be copied to it...
// raw idea: TOCHECK if addresses are aligned we have few bits left...
if (address == 0)
return NULL;
TexMode0 &tm0 = bpmem.tex[texstage >> 2].texMode0[texstage & 3];
TexMode1 &tm1 = bpmem.tex[texstage >> 2].texMode1[texstage & 3];
int maxlevel = (tm1.max_lod >> 4);
bool UseNativeMips = (tm0.min_filter & 3) && (tm0.min_filter != 8) && g_ActiveConfig.bUseNativeMips;
u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address);
int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16;
int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16;
int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format);
int expandedWidth = (width + bsw) & (~bsw);
int expandedHeight = (height + bsh) & (~bsh);
u64 hash_value = 0;
u32 texID = address;
u64 texHash = 0;
u32 FullFormat = tex_format;
bool TextureisDynamic = false;
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
FullFormat = (tex_format | (tlutfmt << 16));
if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures)
{
texHash = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
{
// WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up)
// tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower.
// This trick (to change the texID depending on the TLUT addr) is a trick to get around
// an issue with metroid prime's fonts, where it has multiple sets of fonts on top of
// each other stored in a single texture, and uses the palette to make different characters
// visible or invisible. Thus, unless we want to recreate the textures for every drawn character,
// we must make sure that texture with different tluts get different IDs.
u64 tlutHash = GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
texHash ^= tlutHash;
if (g_ActiveConfig.bSafeTextureCache)
{
texID = texID ^ ((u32)(tlutHash & 0xFFFFFFFF)) ^ ((u32)((tlutHash >> 32) & 0xFFFFFFFF));
}
}
if (g_ActiveConfig.bSafeTextureCache)
hash_value = texHash;
}
bool skip_texture_create = false;
TexCache::iterator iter = textures.find(texID);
if (iter != textures.end())
{
TCacheEntry &entry = iter->second;
if (!g_ActiveConfig.bSafeTextureCache)
{
if(entry.isRenderTarget || entry.isDynamic)
{
if(!g_ActiveConfig.bCopyEFBToTexture)
{
hash_value = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
{
hash_value ^= GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
}
}
else
{
hash_value = 0;
}
}
else
{
hash_value = ((u32 *)ptr)[0];
}
}
else
{
if(entry.isRenderTarget || entry.isDynamic)
{
if(g_ActiveConfig.bCopyEFBToTexture)
{
hash_value = 0;
}
}
}
if (((entry.isRenderTarget || entry.isDynamic) && hash_value == entry.hash && address == entry.addr)
|| ((address == entry.addr) && (hash_value == entry.hash) && ((int) FullFormat == entry.fmt) && entry.MipLevels >= maxlevel))
{
entry.frameCount = frameCount;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, entry.texture);
GL_REPORT_ERRORD();
entry.SetTextureParameters(tm0,tm1);
entry.isDynamic = false;
return &entry;
}
else
{
// Let's reload the new texture data into the same texture,
// instead of destroying it and having to create a new one.
// Might speed up movie playback very, very slightly.
TextureisDynamic = (entry.isRenderTarget || entry.isDynamic) && !g_ActiveConfig.bCopyEFBToTexture;
if (((!(entry.isRenderTarget || entry.isDynamic) && width == entry.w && height == entry.h && (int)FullFormat == entry.fmt) ||
((entry.isRenderTarget || entry.isDynamic) && entry.w == width && entry.h == height && entry.Scaledw == width && entry.Scaledh == height)))
{
glBindTexture(GL_TEXTURE_2D, entry.texture);
GL_REPORT_ERRORD();
entry.SetTextureParameters(tm0,tm1);
skip_texture_create = true;
}
else
{
entry.Destroy(false);
textures.erase(iter);
}
}
}
// Make an entry in the table
TCacheEntry& entry = textures[texID];
entry.isDynamic = TextureisDynamic;
entry.isRenderTarget = false;
entry.Realw = width;
entry.Realh = height;
PC_TexFormat pcfmt = PC_TEX_FMT_NONE;
if (g_ActiveConfig.bHiresTextures)
{
// Load Custom textures
char texPathTemp[MAX_PATH];
int newWidth = width;
int newHeight = height;
sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, (unsigned int) texHash, tex_format);
pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, tex_format, temp);
if (pcfmt != PC_TEX_FMT_NONE)
{
expandedWidth = width = newWidth;
expandedHeight = height = newHeight;
}
}
if (pcfmt == PC_TEX_FMT_NONE)
pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt);
entry.oldpixel = ((u32 *)ptr)[0];
if (g_ActiveConfig.bSafeTextureCache || entry.isDynamic)
entry.hash = hash_value;
else
{
entry.hash = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF);
((u32 *)ptr)[0] = entry.hash;
}
entry.addr = address;
entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format);
GLenum target = GL_TEXTURE_2D;
if (!skip_texture_create) {
glGenTextures(1, (GLuint *)&entry.texture);
glBindTexture(target, entry.texture);
}
bool isPow2 = !((width & (width - 1)) || (height & (height - 1)));
int TexLevels = (width > height)?width:height;
TexLevels = (isPow2 && UseNativeMips && (maxlevel > 0)) ? (int)(log((double)TexLevels)/log((double)2)) + 1 : (isPow2? 0 : 1);
if(TexLevels > (maxlevel + 1) && maxlevel > 0)
TexLevels = (maxlevel + 1);
entry.MipLevels = maxlevel;
bool GenerateMipmaps = TexLevels > 1 || TexLevels == 0;
entry.bHaveMipMaps = GenerateMipmaps;
int gl_format = 0;
int gl_iformat = 0;
int gl_type = 0;
glGenTextures(1, &texture);
GL_REPORT_ERRORD();
}
void TextureCache::TCacheEntry::Bind(unsigned int stage)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
GL_REPORT_ERRORD();
// TODO: is this already done somewhere else?
TexMode0 &tm0 = bpmem.tex[stage >> 2].texMode0[stage & 3];
TexMode1 &tm1 = bpmem.tex[stage >> 2].texMode1[stage & 3];
SetTextureParameters(tm0, tm1);
}
bool TextureCache::TCacheEntry::Save(const char filename[])
{
// TODO: make ogl dump PNGs
std::string tga_filename(filename);
tga_filename.replace(tga_filename.size() - 3, 3, "tga");
return SaveTexture(tga_filename.c_str(), GL_TEXTURE_2D, texture, w, h);
}
TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width,
unsigned int height, unsigned int expanded_width,
unsigned int tex_levels, PC_TexFormat pcfmt)
{
int gl_format = 0,
gl_iformat = 0,
gl_type = 0;
if (pcfmt != PC_TEX_FMT_DXT1)
{
switch (pcfmt)
@ -441,360 +148,148 @@ TextureCache::TCacheEntry* TextureCache::Load(int texstage, u32 address, int wid
gl_iformat = 4;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_RGBA32:
gl_format = GL_RGBA;
gl_iformat = 4;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_I4_AS_I8:
gl_format = GL_LUMINANCE;
gl_iformat = GL_INTENSITY4;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_IA4_AS_IA8:
gl_format = GL_LUMINANCE_ALPHA;
gl_iformat = GL_LUMINANCE4_ALPHA4;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_I8:
gl_format = GL_LUMINANCE;
gl_iformat = GL_INTENSITY8;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_IA8:
gl_format = GL_LUMINANCE_ALPHA;
gl_iformat = GL_LUMINANCE8_ALPHA8;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_RGB565:
gl_format = GL_RGB;
gl_iformat = GL_RGB;
gl_type = GL_UNSIGNED_SHORT_5_6_5;
break;
}
if (expandedWidth != width)
glPixelStorei(GL_UNPACK_ROW_LENGTH, expandedWidth);
//generate mipmaps even if we use native mips to suport textures with less levels
if(skip_texture_create)
{
glTexSubImage2D(target, 0,0,0,width, height, gl_format, gl_type, temp);
}
else
{
if (GenerateMipmaps)
{
if(UseNativeMips)
{
glTexImage2D(target, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexImage2D(target, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
}
}
else
{
glTexImage2D(target, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp);
}
}
if (expandedWidth != width) // reset
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
else
{
if (skip_texture_create)
{
glCompressedTexSubImage2D(target, 0,0,0,width, height,
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,expandedWidth*expandedHeight/2, temp);
}
else
{
glCompressedTexImage2D(target, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
width, height, 0, expandedWidth*expandedHeight/2, temp);
}
}
GL_REPORT_ERRORD();
if(TexLevels > 1 && pcfmt != PC_TEX_FMT_NONE)
{
int level = 1;
int mipWidth = width >> 1;
int mipHeight = height >> 1;
ptr += entry.size_in_bytes;
while((mipHeight || mipWidth) && (level < TexLevels))
{
u32 currentWidth = (mipWidth > 0)? mipWidth : 1;
u32 currentHeight = (mipHeight > 0)? mipHeight : 1;
expandedWidth = (currentWidth + bsw) & (~bsw);
expandedHeight = (currentHeight + bsh) & (~bsh);
TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt);
if (pcfmt != PC_TEX_FMT_DXT1)
{
if (expandedWidth != (int)currentWidth)
glPixelStorei(GL_UNPACK_ROW_LENGTH, expandedWidth);
glTexImage2D(target, level, gl_iformat, currentWidth, currentHeight, 0, gl_format, gl_type, temp);
if (expandedWidth != (int)currentWidth)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
else
{
glCompressedTexImage2D(target, level, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, currentWidth, currentHeight, 0, expandedWidth*expandedHeight/2, temp);
}
GL_REPORT_ERRORD();
u32 size = (max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1;
ptr += size;
mipWidth >>= 1;
mipHeight >>= 1;
level++;
}
}
entry.frameCount = frameCount;
entry.w = width;
entry.h = height;
entry.Scaledw = width;
entry.Scaledh = height;
entry.fmt = FullFormat;
entry.SetTextureParameters(tm0,tm1);
if (g_ActiveConfig.bDumpTextures)
{
// dump texture to file
char szTemp[MAX_PATH];
char szDir[MAX_PATH];
const char* uniqueId = globals->unique_id;
static bool bCheckedDumpDir = false;
sprintf(szDir, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX), uniqueId);
if (!bCheckedDumpDir)
{
if (!File::Exists(szDir) || !File::IsDirectory(szDir))
File::CreateDir(szDir);
bCheckedDumpDir = true;
}
sprintf(szTemp, "%s/%s_%08x_%i.tga", szDir, uniqueId, (unsigned int) texHash, tex_format);
if (!File::Exists(szTemp))
SaveTexture(szTemp, target, entry.texture, entry.w, entry.h);
}
INCSTAT(stats.numTexturesCreated);
SETSTAT(stats.numTexturesAlive, textures.size());
TCacheEntry &entry = *new TCacheEntry;
entry.gl_format = gl_format;
entry.gl_iformat = gl_iformat;
entry.gl_type = gl_type;
entry.pcfmt = pcfmt;
entry.bHaveMipMaps = tex_levels != 1;
return &entry;
}
void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source_rect)
void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level)
{
DVSTARTPROFILE();
GL_REPORT_ERRORD();
//glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
//GL_REPORT_ERRORD();
// for intensity values, use Y of YUV format!
// for all purposes, treat 4bit equivalents as 8bit (probably just used for compression)
// RGBA8 - RGBA8
// RGB565 - RGB565
// RGB5A3 - RGB5A3
// I4,R4,Z4 - I4
// IA4,RA4 - IA4
// Z8M,G8,I8,A8,Z8,R8,B8,Z8L - I8
// Z16,GB8,RG8,Z16L,IA8,RA8 - IA8
float colmat[16];
float fConstAdd[4] = {0};
memset(colmat, 0, sizeof(colmat));
if (bFromZBuffer)
if (pcfmt != PC_TEX_FMT_DXT1)
{
switch(copyfmt)
if (expanded_width != width)
glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width);
if (bHaveMipMaps && 0 == level)
{
case 0: // Z4
case 1: // Z8
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
break;
case 3: // Z16 //?
colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
break;
case 11: // Z16 (reverse order)
colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1;
break;
case 6: // Z24X8
colmat[2] = colmat[5] = colmat[8] = colmat[15] = 1;
break;
case 9: // Z8M
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
break;
case 10: // Z8L
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
break;
case 12: // Z16L
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
}
}
else if (bIsIntensityFmt)
{
// TODO - verify these coefficients
fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f;
colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f;
colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f;
colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f;
if (copyfmt < 2)
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexImage2D(GL_TEXTURE_2D, level, gl_iformat, width, height, 0, gl_format, gl_type, temp);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
}
else
{
fConstAdd[3] = 16.0f / 255.0f;
colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f;
}
else// alpha
colmat[15] = 1;
}
else
{
switch (copyfmt)
{
case 0: // R4
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
break;
case 8: // R8
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
break;
case 2: // RA4
colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1;
break;
case 3: // RA8
colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1;
break;
case 7: // A8
colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1;
break;
case 9: // G8
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
break;
case 10: // B8
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
break;
case 11: // RG8
colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
break;
case 12: // GB8
colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
break;
case 4: // RGB565
colmat[0] = colmat[5] = colmat[10] = 1;
fConstAdd[3] = 1; // set alpha to 1
break;
case 5: // RGB5A3
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
case 6: // RGBA8
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
default:
ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt);
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
break;
}
}
glTexImage2D(GL_TEXTURE_2D, level, gl_iformat, width, height, 0, gl_format, gl_type, temp);
}
bool bIsInit = textures.find(address) != textures.end();
PRIM_LOG("copytarg: addr=0x%x, fromz=%d, intfmt=%d, copyfmt=%d", address, (int)bFromZBuffer, (int)bIsIntensityFmt,copyfmt);
TCacheEntry& entry = textures[address];
entry.hash = 0;
entry.frameCount = frameCount;
int w = (abs(source_rect.GetWidth()) >> bScaleByHalf);
int h = (abs(source_rect.GetHeight()) >> bScaleByHalf);
float xScale = Renderer::GetTargetScaleX();
float yScale = Renderer::GetTargetScaleY();
int Scaledtex_w = (g_ActiveConfig.bCopyEFBScaled)?((int)(xScale * w)) : w;
int Scaledtex_h = (g_ActiveConfig.bCopyEFBScaled)?((int)(yScale * h)) : h;
GLenum gl_format = GL_RGBA;
GLenum gl_iformat = 4;
GLenum gl_type = GL_UNSIGNED_BYTE;
GL_REPORT_ERRORD();
if (!bIsInit)
{
glGenTextures(1, (GLuint *)&entry.texture);
glBindTexture(GL_TEXTURE_2D, entry.texture);
GL_REPORT_ERRORD();
glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, Scaledtex_w, Scaledtex_h, 0, gl_format, gl_type, NULL);
GL_REPORT_ERRORD();
entry.isRenderTarget = true;
entry.isDynamic = false;
if (expanded_width != width)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
else
{
_assert_(entry.texture);
GL_REPORT_ERRORD();
if(entry.isDynamic)
{
Scaledtex_h = h;
Scaledtex_w = w;
}
if (((entry.isRenderTarget || entry.isDynamic) && entry.Scaledw == Scaledtex_w && entry.Scaledh == Scaledtex_h))
{
glBindTexture(GL_TEXTURE_2D, entry.texture);
// for some reason mario sunshine errors here...
// Beyond Good and Evil does too, occasionally.
GL_REPORT_ERRORD();
} else {
// Delete existing texture.
glDeleteTextures(1,(GLuint *)&entry.texture);
glGenTextures(1, (GLuint *)&entry.texture);
glBindTexture(GL_TEXTURE_2D, entry.texture);
glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, Scaledtex_w, Scaledtex_h, 0, gl_format, gl_type, NULL);
GL_REPORT_ERRORD();
entry.isRenderTarget = !entry.isDynamic;
}
PanicAlert("PC_TEX_FMT_DXT1 support disabled");
//glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
//width, height, 0, expanded_width * expanded_height/2, temp);
}
GL_REPORT_ERRORD();
}
if (!bIsInit)
TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
unsigned int scaled_tex_w, unsigned int scaled_tex_h)
{
TCacheEntry *const entry = new TCacheEntry;
glBindTexture(GL_TEXTURE_2D, entry->texture);
GL_REPORT_ERRORD();
const GLenum
gl_format = GL_RGBA,
gl_iformat = 4,
gl_type = GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, scaled_tex_w, scaled_tex_h, 0, gl_format, gl_type, NULL);
GL_REPORT_ERRORD();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (GL_REPORT_ERROR() != GL_NO_ERROR)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (GL_REPORT_ERROR() != GL_NO_ERROR) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
GL_REPORT_ERRORD();
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
GL_REPORT_ERRORD();
}
entry.addr = address;
entry.w = entry.Realw = w;
entry.h = entry.Realh = h;
entry.Scaledw = Scaledtex_w;
entry.Scaledh = Scaledtex_h;
entry.fmt = copyfmt;
entry.hash = 0;
return entry;
}
void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf,
unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect,
bool bIsIntensityFmt, u32 copyfmt)
{
glBindTexture(GL_TEXTURE_2D, texture);
// Make sure to resolve anything we need to read from.
GLuint read_texture = bFromZBuffer ? g_framebufferManager.ResolveAndGetDepthTarget(source_rect) : g_framebufferManager.ResolveAndGetRenderTarget(source_rect);
const GLuint read_texture = bFromZBuffer ?
g_framebufferManager.ResolveAndGetDepthTarget(source_rect) :
g_framebufferManager.ResolveAndGetRenderTarget(source_rect);
// TODO: move
const float xScale = Renderer::GetTargetScaleX();
const float yScale = Renderer::GetTargetScaleY();
GL_REPORT_ERRORD();
// We have to run a pixel shader, for color conversion.
Renderer::ResetAPIState(); // reset any game specific settings
if(!entry.isDynamic || g_ActiveConfig.bCopyEFBToTexture)
if (false == isDynamic || g_ActiveConfig.bCopyEFBToTexture)
{
if (s_TempFramebuffer == 0)
glGenFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer);
glGenFramebuffersEXT(1, (GLuint*)&s_TempFramebuffer);
g_framebufferManager.SetFramebuffer(s_TempFramebuffer);
// Bind texture to temporary framebuffer
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, entry.texture, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0);
GL_REPORT_FBO_ERROR();
GL_REPORT_ERRORD();
@ -803,9 +298,10 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture);
glViewport(0, 0, Scaledtex_w, Scaledtex_h);
glViewport(0, 0, scaledW, scaledH);
PixelShaderCache::SetCurrentShader(bFromZBuffer ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram());
const float* const fConstAdd = colmat + 16; // fConstAdd is the last 4 floats of colmat
PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation
GL_REPORT_ERRORD();
@ -824,10 +320,10 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);
}
if(!g_ActiveConfig.bCopyEFBToTexture)
if (false == g_ActiveConfig.bCopyEFBToTexture)
{
textures[address].hash = TextureConverter::EncodeToRamFromTexture(
address,
hash = TextureConverter::EncodeToRamFromTexture(
addr,
read_texture,
xScale,
yScale,
@ -836,32 +332,69 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo
copyfmt,
bScaleByHalf,
source_rect);
}
// Return to the EFB.
g_framebufferManager.SetFramebuffer(0);
Renderer::RestoreAPIState();
VertexShaderManager::SetViewportChanged();
TextureCache::DisableStage(0);
DisableStage(0);
GL_REPORT_ERRORD();
if (g_ActiveConfig.bDumpEFBTarget)
{
if (g_ActiveConfig.bDumpEFBTarget)
{
static int count = 0;
SaveTexture(StringFromFormat("%sefb_frame_%i.tga", File::GetUserPath(D_DUMPTEXTURES_IDX), count++).c_str(), GL_TEXTURE_2D, entry.texture, entry.w, entry.h);
}
SaveTexture(StringFromFormat("%sefb_frame_%i.tga", File::GetUserPath(D_DUMPTEXTURES_IDX),
count++).c_str(), GL_TEXTURE_2D, texture, w, h);
}
}
void TextureCache::DisableStage(int stage)
void TextureCache::TCacheEntry::SetTextureParameters(const TexMode0 &newmode, const TexMode1 &newmode1)
{
// TODO: not used anywhere
TexMode0 mode = newmode;
//mode1 = newmode1;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
(newmode.mag_filter || g_Config.bForceFiltering) ? GL_LINEAR : GL_NEAREST);
if (bHaveMipMaps)
{
// TODO: not used anywhere
if (g_ActiveConfig.bForceFiltering && newmode.min_filter < 4)
mode.min_filter += 4; // take equivalent forced linear
int filt = newmode.min_filter;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c_MinLinearFilter[filt & 7]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, newmode1.min_lod >> 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, newmode1.max_lod >> 4);
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, (newmode.lod_bias / 32.0f));
}
else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
(g_ActiveConfig.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c_WrapSettings[newmode.wrap_s]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c_WrapSettings[newmode.wrap_t]);
if (g_Config.iMaxAnisotropy >= 1)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
(float)(1 << g_ActiveConfig.iMaxAnisotropy));
}
TextureCache::~TextureCache()
{
if (s_TempFramebuffer)
{
glDeleteFramebuffersEXT(1, (GLuint*)&s_TempFramebuffer);
s_TempFramebuffer = 0;
}
}
void TextureCache::DisableStage(unsigned int stage)
{
glActiveTexture(GL_TEXTURE0 + stage);
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_RECTANGLE_ARB);
}
void TextureCache::ClearRenderTargets()
{
for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter)
iter->second.isRenderTarget = false;
}

View File

@ -24,61 +24,62 @@
#include "GLUtil.h"
#include "BPStructs.h"
class TextureCache
#include "TextureCacheBase.h"
namespace OGL
{
class TextureCache : public ::TextureCache
{
public:
struct TCacheEntry
{
TCacheEntry() : texture(0), addr(0), size_in_bytes(0), hash(0),
w(0), h(0), MipLevels(0),Scaledw(0), Scaledh(0),
isRenderTarget(false), isDynamic(false), bHaveMipMaps(false)
{ mode.hex = 0xFCFCFCFC; }
GLuint texture;
u32 addr;
u32 size_in_bytes;
u64 hash;
u32 paletteHash;
u32 oldpixel; // used for simple cleanup
TexMode0 mode; // current filter and clamp modes that texture is set to
TexMode1 mode1; // current filter and clamp modes that texture is set to
int frameCount;
int w, h, fmt,MipLevels, Realw, Realh;
int Scaledw, Scaledh;
bool isRenderTarget; // if render texture, then rendertex is filled with the direct copy of the render target
// later conversions would have to convert properly from rendertexfmt to texfmt
bool isDynamic; // modified from cpu
bool bHaveMipMaps;
void SetTextureParameters(TexMode0& newmode,TexMode1 &newmode1);
void Destroy(bool shutdown);
void ConvertFromRenderTarget(u32 taddr, int twidth, int theight, int tformat, int tlutaddr, int tlutfmt);
int IntersectsMemoryRange(u32 range_address, u32 range_size);
};
static void DisableStage(unsigned int stage);
private:
typedef std::map<u32, TCacheEntry> TexCache;
struct TCacheEntry : TCacheEntryBase
{
GLuint texture;
static u8 *temp;
static TexCache textures;
PC_TexFormat pcfmt;
public:
static void Init();
static void Cleanup();
static void Shutdown();
static void Invalidate(bool shutdown);
static void InvalidateRange(u32 start_address, u32 size);
static void MakeRangeDynamic(u32 start_address, u32 size);
static TCacheEntry* Load(int texstage, u32 address, int width, int height, u32 format, int tlutaddr, int tlutfmt);
static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source);
int gl_format;
int gl_iformat;
int gl_type;
static void DisableStage(int stage); // sets active texture
bool bHaveMipMaps;
static void ClearRenderTargets(); // sets render target value of all textures to false
//TexMode0 mode; // current filter and clamp modes that texture is set to
//TexMode1 mode1; // current filter and clamp modes that texture is set to
TCacheEntry();
~TCacheEntry();
void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level);
void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf,
unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect,
bool bIsIntensityFmt, u32 copyfmt);
void Bind(unsigned int stage);
bool Save(const char filename[]);
private:
void SetTextureParameters(const TexMode0 &newmode, const TexMode1 &newmode1);
};
~TextureCache();
TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt);
TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h);
private:
bool isOGL() { return true; }
};
bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int height);
}
#endif // _TEXTUREMNGR_H_

View File

@ -34,6 +34,8 @@
namespace TextureConverter
{
using OGL::TextureCache;
static GLuint s_texConvFrameBuffer = 0;
static GLuint s_srcTexture = 0; // for decoding from RAM
static GLuint s_srcTextureWidth = 0;

View File

@ -154,23 +154,25 @@ void VertexManager::vFlush()
{
glActiveTexture(GL_TEXTURE0 + i);
FourTexUnits &tex = bpmem.tex[i >> 2];
TextureCache::TCacheEntry* tentry = TextureCache::Load(i,
TextureCache::TCacheEntryBase* tentry = TextureCache::Load(i,
(tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5,
tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1,
tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9,
tex.texTlut[i&3].tlut_format);
tex.texTlut[i&3].tlut_format,
(tex.texMode0[i&3].min_filter & 3) && (tex.texMode0[i&3].min_filter != 8) && g_ActiveConfig.bUseNativeMips,
(tex.texMode1[i&3].max_lod >> 4));
if (tentry)
{
// 0s are probably for no manual wrapping needed.
PixelShaderManager::SetTexDims(i, tentry->Realw, tentry->Realh, 0, 0);
PixelShaderManager::SetTexDims(i, tentry->nativeW, tentry->nativeH, 0, 0);
if (g_ActiveConfig.iLog & CONF_SAVETEXTURES)
{
// save the textures
char strfile[255];
sprintf(strfile, "%stex%.3d_%d.tga", File::GetUserPath(D_DUMPFRAMES_IDX), g_Config.iSaveTargetId, i);
SaveTexture(strfile, GL_TEXTURE_2D, tentry->texture, tentry->w, tentry->h);
tentry->Save(strfile);
}
}
else

View File

@ -223,7 +223,7 @@ void Video_Prepare()
CommandProcessor::Init();
PixelEngine::Init();
TextureCache::Init();
g_texture_cache = new OGL::TextureCache;
BPInit();
g_vertex_manager = new OGL::VertexManager;
@ -267,7 +267,7 @@ void Shutdown()
PixelShaderManager::Shutdown();
PixelShaderCache::Shutdown();
delete g_vertex_manager;
TextureCache::Shutdown();
delete g_texture_cache;
OpcodeDecoder_Shutdown();
Renderer::Shutdown();
OpenGL_Shutdown();