VideoCommon: create native texture pool
We often need the same native texture objects for new textures. This commit try to avoid destroying and creation of this textures by pooling them. This should be a big performance gain for some efb2ram games as they may overwrites partially a cached texture (which would be deleted) and afterwards try to read it. Creating/destroying sounds like an easy task, but it isn't. eg the nvidia ogl driver synchonize their threads do avoid use-after-free issues.
This commit is contained in:
parent
f4056978c8
commit
6cece6b486
|
@ -30,6 +30,8 @@ unsigned int TextureCache::temp_size;
|
||||||
|
|
||||||
TextureCache::TexCache TextureCache::textures;
|
TextureCache::TexCache TextureCache::textures;
|
||||||
|
|
||||||
|
TextureCache::TexPool TextureCache::texPool;
|
||||||
|
|
||||||
TextureCache::BackupConfig TextureCache::backup_config;
|
TextureCache::BackupConfig TextureCache::backup_config;
|
||||||
|
|
||||||
bool invalidate_texture_cache_requested;
|
bool invalidate_texture_cache_requested;
|
||||||
|
@ -68,6 +70,13 @@ void TextureCache::Invalidate()
|
||||||
delete iter->second;
|
delete iter->second;
|
||||||
|
|
||||||
textures.clear();
|
textures.clear();
|
||||||
|
|
||||||
|
TexPool::iterator
|
||||||
|
iter2 = texPool.begin(),
|
||||||
|
tcend2 = texPool.end();
|
||||||
|
for (; iter2 != tcend2; ++iter2)
|
||||||
|
delete iter2->second;
|
||||||
|
texPool.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureCache::~TextureCache()
|
TextureCache::~TextureCache()
|
||||||
|
@ -135,7 +144,7 @@ void TextureCache::Cleanup()
|
||||||
// EFB copies living on the host GPU are unrecoverable and thus shouldn't be deleted
|
// EFB copies living on the host GPU are unrecoverable and thus shouldn't be deleted
|
||||||
&& ! iter->second->IsEfbCopy() )
|
&& ! iter->second->IsEfbCopy() )
|
||||||
{
|
{
|
||||||
delete iter->second;
|
PoolTexture(iter->second);
|
||||||
textures.erase(iter++);
|
textures.erase(iter++);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -143,6 +152,21 @@ void TextureCache::Cleanup()
|
||||||
++iter;
|
++iter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TexPool::iterator iter2 = texPool.begin();
|
||||||
|
TexPool::iterator tcend2 = texPool.end();
|
||||||
|
while (iter2 != tcend2)
|
||||||
|
{
|
||||||
|
if (frameCount > TEXTURE_KILL_THRESHOLD + iter2->second->frameCount)
|
||||||
|
{
|
||||||
|
delete iter2->second;
|
||||||
|
texPool.erase(iter2++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++iter2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCache::InvalidateRange(u32 start_address, u32 size)
|
void TextureCache::InvalidateRange(u32 start_address, u32 size)
|
||||||
|
@ -155,7 +179,7 @@ void TextureCache::InvalidateRange(u32 start_address, u32 size)
|
||||||
const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size);
|
const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size);
|
||||||
if (0 == rangePosition)
|
if (0 == rangePosition)
|
||||||
{
|
{
|
||||||
delete iter->second;
|
PoolTexture(iter->second);
|
||||||
textures.erase(iter++);
|
textures.erase(iter++);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -215,7 +239,7 @@ void TextureCache::ClearRenderTargets()
|
||||||
{
|
{
|
||||||
if (iter->second->type == TCET_EC_VRAM)
|
if (iter->second->type == TCET_EC_VRAM)
|
||||||
{
|
{
|
||||||
delete iter->second;
|
PoolTexture(iter->second);
|
||||||
textures.erase(iter++);
|
textures.erase(iter++);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -426,8 +450,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// delete the texture and make a new one
|
// pool the texture and make a new one
|
||||||
delete entry;
|
PoolTexture(entry);
|
||||||
entry = NULL;
|
entry = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,8 +469,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
|
||||||
expandedWidth = width;
|
expandedWidth = width;
|
||||||
expandedHeight = height;
|
expandedHeight = height;
|
||||||
|
|
||||||
// If we thought we could reuse the texture before, make sure to delete it now!
|
// If we thought we could reuse the texture before, make sure to pool it now!
|
||||||
delete entry;
|
PoolTexture(entry);
|
||||||
entry = NULL;
|
entry = NULL;
|
||||||
}
|
}
|
||||||
using_custom_texture = true;
|
using_custom_texture = true;
|
||||||
|
@ -473,6 +497,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
|
||||||
const bool use_native_mips = use_mipmaps && !using_custom_lods && (width == nativeW && height == nativeH);
|
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)
|
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)
|
||||||
|
|
||||||
|
// try to search for a pooled texture
|
||||||
|
if (NULL == entry)
|
||||||
|
{
|
||||||
|
// Try to find a matching texture in the pool. We pool unused texture as they often just change the type.
|
||||||
|
// This happens in eg efb2ram which overwrites half of a texture. So most of this textures are only pooled
|
||||||
|
// for some frames.
|
||||||
|
textures[texID] = entry = GetPooledTexture ( width, height, full_format, texLevels, false );
|
||||||
|
}
|
||||||
|
|
||||||
// create the entry/texture
|
// create the entry/texture
|
||||||
if (NULL == entry)
|
if (NULL == entry)
|
||||||
{
|
{
|
||||||
|
@ -821,15 +854,22 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
|
||||||
else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h))
|
else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h))
|
||||||
{
|
{
|
||||||
// remove it and recreate it as a render target
|
// remove it and recreate it as a render target
|
||||||
delete entry;
|
PoolTexture(entry);
|
||||||
entry = NULL;
|
entry = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NULL == entry)
|
||||||
|
{
|
||||||
|
// search for a compatible pooled texture
|
||||||
|
entry = GetPooledTexture(scaled_tex_w, scaled_tex_h, 0, 0, true);
|
||||||
|
|
||||||
if (NULL == entry)
|
if (NULL == entry)
|
||||||
{
|
{
|
||||||
// create the texture
|
// create the texture
|
||||||
textures[dstAddr] = entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h);
|
entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h);
|
||||||
|
}
|
||||||
|
textures[dstAddr] = entry;
|
||||||
|
|
||||||
// TODO: Using the wrong dstFormat, dumb...
|
// TODO: Using the wrong dstFormat, dumb...
|
||||||
entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1);
|
entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1);
|
||||||
|
@ -842,3 +882,33 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
|
||||||
|
|
||||||
entry->FromRenderTarget(dstAddr, dstFormat, srcFormat, srcRect, isIntensity, scaleByHalf, cbufid, colmat);
|
entry->FromRenderTarget(dstAddr, dstFormat, srcFormat, srcRect, isIntensity, scaleByHalf, cbufid, colmat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextureCache::TCacheEntryBase* TextureCache::GetPooledTexture ( u32 width, u32 height, u32 full_format, u32 maxlevel, bool isEfbCopy )
|
||||||
|
{
|
||||||
|
TCacheEntryBase* entry = NULL;
|
||||||
|
std::pair<TexPool::iterator, TexPool::iterator> bounds;
|
||||||
|
bounds = texPool.equal_range(std::make_pair(width, height));
|
||||||
|
while(!entry && bounds.first != bounds.second) {
|
||||||
|
entry = bounds.first->second;
|
||||||
|
if (
|
||||||
|
(isEfbCopy && entry->IsEfbCopy()) ||
|
||||||
|
(!isEfbCopy && entry->type == TCET_NORMAL && full_format == entry->format && entry->num_mipmaps == maxlevel) ||
|
||||||
|
(!isEfbCopy && entry->type == TCET_EC_DYNAMIC)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
texPool.erase(bounds.first);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry = NULL;
|
||||||
|
bounds.first++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureCache::PoolTexture(TextureCache::TCacheEntryBase* entry)
|
||||||
|
{
|
||||||
|
entry->frameCount = frameCount;
|
||||||
|
texPool.insert(std::make_pair(std::make_pair(entry->virtual_width, entry->virtual_height), entry));
|
||||||
|
}
|
||||||
|
|
|
@ -121,9 +121,13 @@ private:
|
||||||
static void DumpTexture(TCacheEntryBase* entry, unsigned int level);
|
static void DumpTexture(TCacheEntryBase* entry, unsigned int level);
|
||||||
|
|
||||||
typedef std::map<u32, TCacheEntryBase*> TexCache;
|
typedef std::map<u32, TCacheEntryBase*> TexCache;
|
||||||
|
|
||||||
static TexCache textures;
|
static TexCache textures;
|
||||||
|
|
||||||
|
static TCacheEntryBase* GetPooledTexture(u32 width, u32 height, u32 full_format, u32 maxlevel, bool isEfbCopy);
|
||||||
|
static void PoolTexture(TCacheEntryBase *entry);
|
||||||
|
typedef std::multimap<std::pair<u32,u32>, TCacheEntryBase*> TexPool;
|
||||||
|
static TexPool texPool;
|
||||||
|
|
||||||
// Backup configuration values
|
// Backup configuration values
|
||||||
static struct BackupConfig
|
static struct BackupConfig
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue