Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
degasus committed Nov 5, 2013
1 parent f405697 commit 6cece6b
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 12 deletions.
92 changes: 81 additions & 11 deletions Source/Core/VideoCommon/Src/TextureCacheBase.cpp
Expand Up @@ -30,6 +30,8 @@ unsigned int TextureCache::temp_size;

TextureCache::TexCache TextureCache::textures;

TextureCache::TexPool TextureCache::texPool;

TextureCache::BackupConfig TextureCache::backup_config;

bool invalidate_texture_cache_requested;
Expand Down Expand Up @@ -68,6 +70,13 @@ void TextureCache::Invalidate()
delete iter->second;

textures.clear();

TexPool::iterator
iter2 = texPool.begin(),
tcend2 = texPool.end();
for (; iter2 != tcend2; ++iter2)
delete iter2->second;
texPool.clear();
}

TextureCache::~TextureCache()
Expand Down Expand Up @@ -135,14 +144,29 @@ void TextureCache::Cleanup()
// EFB copies living on the host GPU are unrecoverable and thus shouldn't be deleted
&& ! iter->second->IsEfbCopy() )
{
delete iter->second;
PoolTexture(iter->second);
textures.erase(iter++);
}
else
{
++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)
Expand All @@ -155,7 +179,7 @@ void TextureCache::InvalidateRange(u32 start_address, u32 size)
const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size);
if (0 == rangePosition)
{
delete iter->second;
PoolTexture(iter->second);
textures.erase(iter++);
}
else
Expand Down Expand Up @@ -215,7 +239,7 @@ void TextureCache::ClearRenderTargets()
{
if (iter->second->type == TCET_EC_VRAM)
{
delete iter->second;
PoolTexture(iter->second);
textures.erase(iter++);
}
else
Expand Down Expand Up @@ -426,8 +450,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
}
else
{
// delete the texture and make a new one
delete entry;
// pool the texture and make a new one
PoolTexture(entry);
entry = NULL;
}
}
Expand All @@ -445,8 +469,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage,
expandedWidth = width;
expandedHeight = height;

// If we thought we could reuse the texture before, make sure to delete it now!
delete entry;
// If we thought we could reuse the texture before, make sure to pool it now!
PoolTexture(entry);
entry = NULL;
}
using_custom_texture = true;
Expand All @@ -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);
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
if (NULL == entry)
{
Expand Down Expand Up @@ -821,16 +854,23 @@ 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))
{
// remove it and recreate it as a render target
delete entry;
PoolTexture(entry);
entry = NULL;
}
}

if (NULL == entry)
{
// create the texture
textures[dstAddr] = entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h);

// search for a compatible pooled texture
entry = GetPooledTexture(scaled_tex_w, scaled_tex_h, 0, 0, true);

if (NULL == entry)
{
// create the texture
entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h);
}
textures[dstAddr] = entry;

// TODO: Using the wrong dstFormat, dumb...
entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1);
entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h);
Expand All @@ -842,3 +882,33 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat

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));
}
6 changes: 5 additions & 1 deletion Source/Core/VideoCommon/Src/TextureCacheBase.h
Expand Up @@ -121,8 +121,12 @@ class TextureCache
static void DumpTexture(TCacheEntryBase* entry, unsigned int level);

typedef std::map<u32, TCacheEntryBase*> TexCache;

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
static struct BackupConfig
Expand Down

0 comments on commit 6cece6b

Please sign in to comment.