TextureCache: Add content locking

Texture cache occasionally mutates textures for efficiency.
Which is awkward if we want to borrow those textures from texture cache
to do something else, such as a graphics debugger, or async presentation
on another thread.

Content locking provides a way to signal that the contents of a texture
cache entry should not change. Texture cache will be forced to use
alternative strategies.
This commit is contained in:
Scott Mansell 2022-07-27 19:35:51 +12:00
parent 606c18210d
commit a01d5283ec
2 changed files with 39 additions and 17 deletions

View File

@ -700,7 +700,8 @@ void TextureCacheBase::DoLoadState(PointerWrap& p)
// Even if the texture isn't valid, we still need to create the cache entry object
// to update the point in the state state. We'll just throw it away if it's invalid.
auto tex = DeserializeTexture(p);
auto entry = Common::make_rc<TCacheEntry>(std::move(tex->texture), std::move(tex->framebuffer));
auto entry =
std::make_shared<TCacheEntry>(std::move(tex->texture), std::move(tex->framebuffer));
entry->textures_by_hash_iter = textures_by_hash.end();
entry->DoState(p);
if (entry->texture && commit_state)
@ -809,6 +810,13 @@ RcTcacheEntry TextureCacheBase::DoPartialTextureUpdates(RcTcacheEntry& entry_to_
if (entry_to_update->IsCopy())
return entry_to_update;
if (entry_to_update->IsLocked())
{
// TODO: Shouldn't be too hard, just need to clone the texture entry + texture contents.
PanicAlertFmt("TextureCache: PartialTextureUpdates of locked textures is not implemented");
return {};
}
u32 block_width = TexDecoder_GetBlockWidthInTexels(entry_to_update->format.texfmt);
u32 block_height = TexDecoder_GetBlockHeightInTexels(entry_to_update->format.texfmt);
u32 block_size = block_width * block_height *
@ -1474,8 +1482,11 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
{
entry = DoPartialTextureUpdates(iter->second, texture_info.GetTlutAddress(),
texture_info.GetTlutFormat());
entry->texture->FinishedRendering();
return entry;
if (entry)
{
entry->texture->FinishedRendering();
return entry;
}
}
}
@ -1511,9 +1522,8 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
if (unconverted_copy != textures_by_address.end())
{
auto decoded_entry =
ApplyPaletteToEntry(unconverted_copy->second, texture_info.GetTlutAddress(),
texture_info.GetTlutFormat());
auto decoded_entry = ApplyPaletteToEntry(
unconverted_copy->second, texture_info.GetTlutAddress(), texture_info.GetTlutFormat());
if (decoded_entry)
{
@ -1543,8 +1553,11 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
{
entry = DoPartialTextureUpdates(hash_iter->second, texture_info.GetTlutAddress(),
texture_info.GetTlutFormat());
entry->texture->FinishedRendering();
return entry;
if (entry)
{
entry->texture->FinishedRendering();
return entry;
}
}
++hash_iter;
}
@ -1771,9 +1784,9 @@ RcTcacheEntry TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height
return {};
}
// Do we currently have a version of this XFB copy in VRAM?
// Do we currently have a mutable version of this XFB copy in VRAM?
RcTcacheEntry entry = GetXFBFromCache(address, width, height, stride);
if (entry)
if (entry && !entry->IsLocked())
{
if (entry->is_xfb_container)
{
@ -2265,8 +2278,8 @@ void TextureCacheBase::CopyRenderTargetToTexture(
entry->may_have_overlapping_textures = false;
entry->is_custom_tex = false;
CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, linear_filter,
dstFormat, isIntensity, gamma, clamp_top, clamp_bottom,
CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, linear_filter, dstFormat,
isIntensity, gamma, clamp_top, clamp_bottom,
GetVRAMCopyFilterCoefficients(filter_coefficients));
if (is_xfb_copy && (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods))
@ -2584,7 +2597,7 @@ RcTcacheEntry TextureCacheBase::AllocateCacheEntry(const TextureConfig& config)
return {};
auto cacheEntry =
Common::make_rc<TCacheEntry>(std::move(alloc->texture), std::move(alloc->framebuffer));
std::make_shared<TCacheEntry>(std::move(alloc->texture), std::move(alloc->framebuffer));
cacheEntry->textures_by_hash_iter = textures_by_hash.end();
cacheEntry->id = last_entry_id++;
return cacheEntry;
@ -2710,7 +2723,8 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe
{
// The texture data has already been copied into the staging texture, so it's valid to
// optimistically release the texture data. Will slightly lower VRAM usage.
ReleaseToPool(entry.get());
if (!entry->IsLocked())
ReleaseToPool(entry.get());
}
}
entry->invalidated = true;

View File

@ -19,7 +19,6 @@
#include "Common/BitSet.h"
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/RcPtr.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/TextureConfig.h"
@ -127,6 +126,7 @@ struct TCacheEntry
bool is_xfb_copy = false;
bool is_xfb_container = false;
u64 id = 0;
u32 content_semaphore = 0; // Counts up
// Indicates that this TCacheEntry has been invalided from textures_by_address
bool invalidated = false;
@ -143,7 +143,7 @@ struct TCacheEntry
// Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when
// removing the cache entry
std::multimap<u64, Common::rc_ptr<TCacheEntry>>::iterator textures_by_hash_iter;
std::multimap<u64, std::shared_ptr<TCacheEntry>>::iterator textures_by_hash_iter;
// This is used to keep track of both:
// * efb copies used by this partially updated texture
@ -194,6 +194,14 @@ struct TCacheEntry
other_entry->references.emplace(this);
}
// Acquiring a content lock will lock the current contents and prevent texture cache from
// reusing the same entry for a newer version of the texture.
void AcquireContentLock() { content_semaphore++; }
void ReleaseContentLock() { content_semaphore--; }
// Can this be mutated?
bool IsLocked() const { return content_semaphore > 0; }
void SetXfbCopy(u32 stride);
void SetEfbCopy(u32 stride);
void SetNotCopy();
@ -217,7 +225,7 @@ struct TCacheEntry
void DoState(PointerWrap& p);
};
using RcTcacheEntry = Common::rc_ptr<TCacheEntry>;
using RcTcacheEntry = std::shared_ptr<TCacheEntry>;
class TextureCacheBase
{