summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/Src/TextureCacheBase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoCommon/Src/TextureCacheBase.cpp')
-rw-r--r--Source/Core/VideoCommon/Src/TextureCacheBase.cpp265
1 files changed, 139 insertions, 126 deletions
diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp
index e0fc11fe1b..319c014f22 100644
--- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp
@@ -56,7 +56,7 @@ TextureCache::TextureCache()
if (!temp)
temp = (u8*)AllocateAlignedMemory(temp_size, 16);
TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
- if(g_ActiveConfig.bHiresTextures && !g_ActiveConfig.bDumpTextures)
+ if(g_ActiveConfig.bHiresTextures && !g_ActiveConfig.bDumpTextures)
HiresTextures::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str());
SetHash64Function(g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures);
}
@@ -84,35 +84,34 @@ TextureCache::~TextureCache()
void TextureCache::OnConfigChanged(VideoConfig& config)
{
- if (!g_texture_cache)
- goto skip_checks;
-
- // TODO: Invalidating texcache is really stupid in some of these cases
- if (config.iSafeTextureCache_ColorSamples != backup_config.s_colorsamples ||
- config.bTexFmtOverlayEnable != backup_config.s_texfmt_overlay ||
- config.bTexFmtOverlayCenter != backup_config.s_texfmt_overlay_center ||
- config.bHiresTextures != backup_config.s_hires_textures)
+ if (g_texture_cache)
{
- g_texture_cache->Invalidate();
+ // TODO: Invalidating texcache is really stupid in some of these cases
+ if (config.iSafeTextureCache_ColorSamples != backup_config.s_colorsamples ||
+ config.bTexFmtOverlayEnable != backup_config.s_texfmt_overlay ||
+ config.bTexFmtOverlayCenter != backup_config.s_texfmt_overlay_center ||
+ config.bHiresTextures != backup_config.s_hires_textures)
+ {
+ g_texture_cache->Invalidate();
- if(g_ActiveConfig.bHiresTextures)
- HiresTextures::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str());
+ if(g_ActiveConfig.bHiresTextures)
+ HiresTextures::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str());
- SetHash64Function(g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures);
- TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
- }
+ SetHash64Function(g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures);
+ TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
+ }
- // TODO: Probably shouldn't clear all render targets here, just mark them dirty or something.
- if (config.bEFBCopyCacheEnable != backup_config.s_copy_cache_enable || // TODO: not sure if this is needed?
- config.bCopyEFBToTexture != backup_config.s_copy_efb_to_texture ||
- config.bCopyEFBScaled != backup_config.s_copy_efb_scaled ||
- config.bEFBCopyEnable != backup_config.s_copy_efb ||
- config.iEFBScale != backup_config.s_efb_scale)
- {
- g_texture_cache->ClearRenderTargets();
+ // TODO: Probably shouldn't clear all render targets here, just mark them dirty or something.
+ if (config.bEFBCopyCacheEnable != backup_config.s_copy_cache_enable || // TODO: not sure if this is needed?
+ config.bCopyEFBToTexture != backup_config.s_copy_efb_to_texture ||
+ config.bCopyEFBScaled != backup_config.s_copy_efb_scaled ||
+ config.bEFBCopyEnable != backup_config.s_copy_efb ||
+ config.iEFBScale != backup_config.s_efb_scale)
+ {
+ g_texture_cache->ClearRenderTargets();
+ }
}
-
-skip_checks:
+
backup_config.s_colorsamples = config.iSafeTextureCache_ColorSamples;
backup_config.s_copy_efb_to_texture = config.bCopyEFBToTexture;
backup_config.s_copy_efb_scaled = config.bCopyEFBScaled;
@@ -130,7 +129,10 @@ void TextureCache::Cleanup()
TexCache::iterator tcend = textures.end();
while (iter != tcend)
{
- if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second->frameCount) // TODO: Deleting EFB copies might not be a good idea here...
+ if ( frameCount > TEXTURE_KILL_THRESHOLD + iter->second->frameCount
+
+ // EFB copies living on the host GPU are unrecoverable and thus shouldn't be deleted
+ && ! iter->second->IsEfbCopy() )
{
delete iter->second;
textures.erase(iter++);
@@ -218,6 +220,9 @@ void TextureCache::ClearRenderTargets()
bool TextureCache::CheckForCustomTextureLODs(u64 tex_hash, int texformat, unsigned int levels)
{
+ if (levels == 1)
+ return false;
+
// Just checking if the necessary files exist, if they can't be loaded or have incorrect dimensions LODs will be black
char texBasePathTemp[MAX_PATH];
char texPathTemp[MAX_PATH];
@@ -298,14 +303,30 @@ void TextureCache::DumpTexture(TCacheEntryBase* entry, unsigned int level)
entry->Save(szTemp, level);
}
-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, bool from_tmem)
+static u32 CalculateLevelSize(u32 level_0_size, u32 level)
+{
+ return (level_0_size + ((1 << level) - 1)) >> level;
+}
+
+// Used by TextureCache::Load
+static TextureCache::TCacheEntryBase* ReturnEntry(unsigned int stage, TextureCache::TCacheEntryBase* entry)
+{
+ entry->frameCount = frameCount;
+ entry->Bind(stage);
+
+ GFX_DEBUGGER_PAUSE_AT(NEXT_TEXTURE_CHANGE, true);
+
+ return entry;
+}
+
+TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
+ u32 const address, unsigned int width, unsigned int height, int const texformat,
+ unsigned int const tlutaddr, int const tlutfmt, bool const use_mipmaps, unsigned int maxlevel, bool const from_tmem)
{
if (0 == address)
return NULL;
- // TexelSizeInNibbles(format)*width*height/16;
+ // TexelSizeInNibbles(format) * width * height / 16;
const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat) - 1;
const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat) - 1;
@@ -314,11 +335,9 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage,
const unsigned int nativeW = width;
const unsigned int nativeH = height;
- bool using_custom_texture = false;
- bool using_custom_lods = false;
-
u32 texID = address;
- u64 tex_hash = TEXHASH_INVALID; // Hash assigned to texcache entry (also used to generate filenames used for texture dumping and custom texture lookup)
+ // Hash assigned to texcache entry (also used to generate filenames used for texture dumping and custom texture lookup)
+ u64 tex_hash = TEXHASH_INVALID;
u64 tlut_hash = TEXHASH_INVALID;
u32 full_format = texformat;
@@ -329,9 +348,12 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage,
full_format = texformat | (tlutfmt << 16);
const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat);
- u8* src_data;
- if (from_tmem) src_data = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE];
- else src_data = Memory::GetPointer(address);
+
+ const u8* src_data;
+ if (from_tmem)
+ src_data = &texMem[bpmem.tex[stage / 4].texImage1[stage % 4].tmem_even * TMEM_LINE_SIZE];
+ else
+ src_data = Memory::GetPointer(address);
// TODO: This doesn't hash GB tiles for preloaded RGBA8 textures (instead, it's hashing more data from the low tmem bank than it should)
tex_hash = GetHash64(src_data, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples);
@@ -353,6 +375,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage,
tex_hash ^= tlut_hash;
}
+ // D3D doesn't like when the specified mipmap count would require more than one 1x1-sized LOD in the mipmap chain
+ // e.g. 64x64 with 7 LODs would have the mipmap chain 64x64,32x32,16x16,8x8,4x4,2x2,1x1,1x1, so we limit the mipmap count to 6 there
+ while (g_ActiveConfig.backend_info.bUseMinimalMipCount && max(expandedWidth, expandedHeight) >> maxlevel == 0)
+ --maxlevel;
+
TCacheEntryBase *entry = textures[texID];
if (entry)
{
@@ -366,15 +393,17 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage,
{
entry->type = TCET_EC_VRAM;
- // TODO: Print a warning if the format changes! In this case, we could reinterpret the internal texture object data to the new pixel format (similiar to what is already being done in Renderer::ReinterpretPixelFormat())
- goto return_entry;
+ // TODO: Print a warning if the format changes! In this case,
+ // we could reinterpret the internal texture object data to the new pixel format
+ // (similiar to what is already being done in Renderer::ReinterpretPixelFormat())
+ return ReturnEntry(stage, entry);
}
// 2. b) For normal textures, all texture parameters need to match
if (address == entry->addr && tex_hash == entry->hash && full_format == entry->format &&
- entry->num_mipmaps == maxlevel && entry->native_width == nativeW && entry->native_height == nativeH)
+ entry->num_mipmaps > maxlevel && entry->native_width == nativeW && entry->native_height == nativeH)
{
- goto return_entry;
+ return ReturnEntry(stage, entry);
}
// 3. If we reach this line, we'll have to upload the new texture data to VRAM.
@@ -382,7 +411,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage,
//
// TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies?
// TODO: Actually, it should be enough if the internal texture format matches...
- if ((entry->type == TCET_NORMAL && width == entry->virtual_width && height == entry->virtual_height && full_format == entry->format && entry->num_mipmaps == maxlevel)
+ if ((entry->type == TCET_NORMAL && width == entry->virtual_width && height == entry->virtual_height
+ && full_format == entry->format && entry->num_mipmaps > maxlevel)
|| (entry->type == TCET_EC_DYNAMIC && entry->native_width == width && entry->native_height == height))
{
// reuse the texture
@@ -395,9 +425,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage,
}
}
+ bool using_custom_texture = false;
if (g_ActiveConfig.bHiresTextures)
{
+ // This function may modify width/height.
pcfmt = LoadCustomTexture(tex_hash, texformat, 0, width, height);
if (pcfmt != PC_TEX_FMT_NONE)
{
@@ -428,115 +460,100 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage,
}
}
- // TODO: Cleanup. Plus, we still autogenerate mipmaps in certain cases (we shouldn't do that)
- bool isPow2;
- unsigned int texLevels;
- isPow2 = !((width & (width - 1)) || (height & (height - 1)));
- texLevels = (isPow2 && maxlevel) ? GetPow2(std::max(width, height)) : !isPow2;
- texLevels = maxlevel ? std::min(texLevels, maxlevel + 1) : texLevels;
- using_custom_lods = using_custom_texture && CheckForCustomTextureLODs(tex_hash, texformat, texLevels);
- UseNativeMips = UseNativeMips && !using_custom_lods && (width == nativeW && height == nativeH); // Only load native mips if their dimensions fit to our virtual texture dimensions
- texLevels = (UseNativeMips || using_custom_lods) ? texLevels : !isPow2;
+ u32 texLevels = use_mipmaps ? (maxlevel + 1) : 1;
+ const bool using_custom_lods = using_custom_texture && CheckForCustomTextureLODs(tex_hash, texformat, texLevels);
+ // Only load native mips if their dimensions fit to our virtual texture dimensions
+ const bool use_native_mips = use_mipmaps && !using_custom_lods && (width == nativeW && height == nativeH);
+ texLevels = (use_native_mips || using_custom_lods) ? texLevels : 1; // TODO: Should be forced to 1 for non-pow2 textures (e.g. efb copies with automatically adjusted IR)
// create the entry/texture
- if (NULL == entry) {
+ if (NULL == entry)
+ {
textures[texID] = entry = g_texture_cache->CreateTexture(width, height, expandedWidth, texLevels, pcfmt);
// Sometimes, we can get around recreating a texture if only the number of mip levels changes
// e.g. if our texture cache entry got too many mipmap levels we can limit the number of used levels by setting the appropriate render states
// Thus, we don't update this member for every Load, but just whenever the texture gets recreated
- //
- // TODO: Won't we end up recreating textures all the time because maxlevel doesn't necessarily equal texLevels?
- entry->num_mipmaps = maxlevel; // TODO: Does this actually work? We can't really adjust mipmap settings per-stage...
+ // TODO: D3D9 doesn't support min_lod. We should add a workaround for that here!
+
+ // TODO: This is the wrong value. We should be storing the number of levels our actual texture has.
+ // But that will currently make the above "existing entry" tests fail as "texLevels" is not calculated until after.
+ // Currently, we might try to reuse a texture which appears to have more levels than actual, maybe..
+ entry->num_mipmaps = maxlevel + 1;
entry->type = TCET_NORMAL;
GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true);
}
+ else
+ {
+ // load texture (CreateTexture also loads level 0)
+ entry->Load(width, height, expandedWidth, 0);
+ }
entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps);
entry->SetDimensions(nativeW, nativeH, width, height);
entry->hash = tex_hash;
- if (entry->IsEfbCopy() && !g_ActiveConfig.bCopyEFBToTexture) entry->type = TCET_EC_DYNAMIC;
- else entry->type = TCET_NORMAL;
-
- // load texture
- entry->Load(width, height, expandedWidth, 0, (texLevels == 0));
+
+ if (entry->IsEfbCopy() && !g_ActiveConfig.bCopyEFBToTexture)
+ entry->type = TCET_EC_DYNAMIC;
+ else
+ entry->type = TCET_NORMAL;
if (g_ActiveConfig.bDumpTextures && !using_custom_texture)
DumpTexture(entry, 0);
+ u32 level = 1;
// load mips - TODO: Loading mipmaps from tmem is untested!
- if (texLevels > 1 && pcfmt != PC_TEX_FMT_NONE && UseNativeMips)
+ if (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;
-
- u8* ptr_even = NULL, *ptr_odd = NULL;
- if (from_tmem)
+ if (use_native_mips)
{
- ptr_even = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE + texture_size];
- ptr_odd = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE];
- }
- src_data += texture_size;
-
- while ((mipHeight || mipWidth) && (level < texLevels))
- {
- u8** ptr;
- if (from_tmem) ptr = (level % 2) ? &ptr_odd : &ptr_even;
- else ptr = &src_data;
-
- 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_ActiveConfig.backend_info.bUseRGBATextures);
- entry->Load(currentWidth, currentHeight, expandedWidth, level, false);
+ src_data += texture_size;
+
+ const u8* ptr_even = NULL;
+ const u8* ptr_odd = NULL;
+ if (from_tmem)
+ {
+ ptr_even = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE + texture_size];
+ ptr_odd = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE];
+ }
- if (g_ActiveConfig.bDumpTextures)
- DumpTexture(entry, level);
+ for (; level != texLevels; ++level)
+ {
+ const u32 mip_width = CalculateLevelSize(width, level);
+ const u32 mip_height = CalculateLevelSize(height, level);
+ const u32 expanded_mip_width = (mip_width + bsw) & (~bsw);
+ const u32 expanded_mip_height = (mip_height + bsh) & (~bsh);
+
+ const u8*& mip_src_data = from_tmem
+ ? ((level % 2) ? ptr_odd : ptr_even)
+ : src_data;
+ TexDecoder_Decode(temp, mip_src_data, expanded_mip_width, expanded_mip_height, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures);
+ mip_src_data += TexDecoder_GetTextureSizeInBytes(expanded_mip_width, expanded_mip_height, texformat);
+
+ entry->Load(mip_width, mip_height, expanded_mip_width, level);
- *ptr += ((std::max(mipWidth, bsw) * std::max(mipHeight, bsh) * bsdepth) >> 1);
- mipWidth >>= 1;
- mipHeight >>= 1;
- ++level;
+ if (g_ActiveConfig.bDumpTextures)
+ DumpTexture(entry, level);
+ }
}
- }
- else if (texLevels > 1 && pcfmt != PC_TEX_FMT_NONE && using_custom_lods)
- {
- unsigned int level = 1;
- unsigned int mipWidth = (width + 1) >> 1;
- unsigned int mipHeight = (height + 1) >> 1;
-
- while ((mipHeight || mipWidth) && (level < texLevels))
+ else if (using_custom_lods)
{
- unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1;
- unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1;
-
- LoadCustomTexture(tex_hash, texformat, level, currentWidth, currentHeight);
- entry->Load(currentWidth, currentHeight, currentWidth, level, false);
+ for (; level != texLevels; ++level)
+ {
+ unsigned int mip_width = CalculateLevelSize(width, level);
+ unsigned int mip_height = CalculateLevelSize(height, level);
- mipWidth >>= 1;
- mipHeight >>= 1;
- ++level;
+ LoadCustomTexture(tex_hash, texformat, level, mip_width, mip_height);
+ entry->Load(mip_width, mip_height, mip_width, level);
+ }
}
}
INCSTAT(stats.numTexturesCreated);
SETSTAT(stats.numTexturesAlive, textures.size());
-return_entry:
-
- entry->frameCount = frameCount;
- entry->Bind(stage);
-
- GFX_DEBUGGER_PAUSE_AT(NEXT_TEXTURE_CHANGE, true);
-
- return entry;
+ return ReturnEntry(stage, entry);
}
void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, unsigned int srcFormat,
@@ -708,7 +725,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
case 1: // R8
case 8: // R8
colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
- cbufid = 13;
+ cbufid = 13;
break;
case 2: // RA4
@@ -729,11 +746,11 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
case 9: // G8
colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f;
- cbufid = 17;
+ cbufid = 17;
break;
case 10: // B8
colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f;
- cbufid = 18;
+ cbufid = 18;
break;
case 11: // RG8
@@ -742,7 +759,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
break;
case 12: // GB8
- colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1.0f;
+ colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1.0f;
cbufid = 20;
break;
@@ -814,9 +831,5 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
entry->frameCount = frameCount;
- g_renderer->ResetAPIState(); // reset any game specific settings
-
entry->FromRenderTarget(dstAddr, dstFormat, srcFormat, srcRect, isIntensity, scaleByHalf, cbufid, colmat);
-
- g_renderer->RestoreAPIState();
}