Skip to content

Commit

Permalink
Centralize SetTexture
Browse files Browse the repository at this point in the history
  • Loading branch information
hrydgard committed Feb 19, 2017
1 parent e24e652 commit f15e25c
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 884 deletions.
217 changes: 216 additions & 1 deletion GPU/Common/TextureCacheCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ TextureCacheCommon::TextureCacheCommon(Draw::DrawContext *draw)
clutTotalBytes_(0),
clutMaxBytes_(0),
clutRenderAddress_(0xFFFFFFFF),
clutAlphaLinear_(false) {
clutAlphaLinear_(false),
isBgraBackend_(false) {
decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL;

// TODO: Clamp down to 256/1KB? Need to check mipmapShareClut and clamp loadclut.
Expand Down Expand Up @@ -172,6 +173,220 @@ void TextureCacheCommon::UpdateMaxSeenV(TexCacheEntry *entry, bool throughMode)
}
}


void TextureCacheCommon::SetTexture(bool force) {
#ifdef DEBUG_TEXTURES
if (SetDebugTexture()) {
// A different texture was bound, let's rebind next time.
InvalidateLastTexture();
return;
}
#endif

if (force) {
InvalidateLastTexture();
}

u8 level = 0;
if (IsFakeMipmapChange())
level = (gstate.texlevel >> 20) & 0xF;
u32 texaddr = gstate.getTextureAddress(level);
if (!Memory::IsValidAddress(texaddr)) {
// Bind a null texture and return.
Unbind();
InvalidateLastTexture();
return;
}

const u16 dim = gstate.getTextureDimension(level);
int w = gstate.getTextureWidth(level);
int h = gstate.getTextureHeight(level);

GETextureFormat format = gstate.getTextureFormat();
if (format >= 11) {
ERROR_LOG_REPORT(G3D, "Unknown texture format %i", format);
// TODO: Better assumption?
format = GE_TFMT_5650;
}
bool hasClut = gstate.isTextureFormatIndexed();

// Ignore uncached/kernel when caching.
u32 cluthash;
if (hasClut) {
if (clutLastFormat_ != gstate.clutformat) {
// We update here because the clut format can be specified after the load.
UpdateCurrentClut(gstate.getClutPaletteFormat(), gstate.getClutIndexStartPos(), gstate.isClutIndexSimple());
}
cluthash = clutHash_ ^ gstate.clutformat;
} else {
cluthash = 0;
}
u64 cachekey = TexCacheEntry::CacheKey(texaddr, format, dim, cluthash);

int bufw = GetTextureBufw(0, texaddr, format);
u8 maxLevel = gstate.getTextureMaxLevel();

u32 texhash = MiniHash((const u32 *)Memory::GetPointerUnchecked(texaddr));

TexCache::iterator iter = cache.find(cachekey);
TexCacheEntry *entry = NULL;
gstate_c.needShaderTexClamp = false;
gstate_c.skipDrawReason &= ~SKIPDRAW_BAD_FB_TEXTURE;
gstate_c.bgraTexture = isBgraBackend_;

if (iter != cache.end()) {
entry = &iter->second;
// Validate the texture still matches the cache entry.
bool match = entry->Matches(dim, format, maxLevel);
const char *reason = "different params";

// Check for FBO - slow!
if (entry->framebuffer) {
if (match) {
if (hasClut && clutRenderAddress_ != 0xFFFFFFFF) {
WARN_LOG_REPORT_ONCE(clutAndTexRender, G3D, "Using rendered texture with rendered CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat());
}

SetTextureFramebuffer(entry, entry->framebuffer);
return;
} else {
// Make sure we re-evaluate framebuffers.
DetachFramebuffer(entry, texaddr, entry->framebuffer);
reason = "detached framebuf";
match = false;
}
}

bool rehash = entry->GetHashStatus() == TexCacheEntry::STATUS_UNRELIABLE;

// First let's see if another texture with the same address had a hashfail.
if (entry->status & TexCacheEntry::STATUS_CLUT_RECHECK) {
// Always rehash in this case, if one changed the rest all probably did.
rehash = true;
entry->status &= ~TexCacheEntry::STATUS_CLUT_RECHECK;
} else if (!gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE)) {
// Okay, just some parameter change - the data didn't change, no need to rehash.
rehash = false;
}

if (match) {
if (entry->lastFrame != gpuStats.numFlips) {
u32 diff = gpuStats.numFlips - entry->lastFrame;
entry->numFrames++;

if (entry->framesUntilNextFullHash < diff) {
// Exponential backoff up to 512 frames. Textures are often reused.
if (entry->numFrames > 32) {
// Also, try to add some "randomness" to avoid rehashing several textures the same frame.
entry->framesUntilNextFullHash = std::min(512, entry->numFrames) + (entry->textureName & 15);
} else {
entry->framesUntilNextFullHash = entry->numFrames;
}
rehash = true;
} else {
entry->framesUntilNextFullHash -= diff;
}
}

// If it's not huge or has been invalidated many times, recheck the whole texture.
if (entry->invalidHint > 180 || (entry->invalidHint > 15 && (dim >> 8) < 9 && (dim & 0xF) < 9)) {
entry->invalidHint = 0;
rehash = true;
}

if (texhash != entry->hash) {
match = false;
} else if (entry->GetHashStatus() == TexCacheEntry::STATUS_RELIABLE) {
rehash = false;
}
}

if (match && (entry->status & TexCacheEntry::STATUS_TO_SCALE) && standardScaleFactor_ != 1 && texelsScaledThisFrame_ < TEXCACHE_MAX_TEXELS_SCALED) {
if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) {
// INFO_LOG(G3D, "Reloading texture to do the scaling we skipped..");
match = false;
reason = "scaling";
}
}

if (match) {
// TODO: Mark the entry reliable if it's been safe for long enough?
//got one!
gstate_c.curTextureWidth = w;
gstate_c.curTextureHeight = h;
if (rehash) {
// Update in case any of these changed.
entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8;
entry->bufw = bufw;
entry->cluthash = cluthash;
}

nextTexture_ = entry;
nextNeedsRehash_ = rehash;
nextNeedsChange_ = false;
// Might need a rebuild if the hash fails.
nextNeedsRebuild_ = false;
VERBOSE_LOG(G3D, "Texture at %08x Found in Cache, applying", texaddr);
return; //Done!
} else {
nextChangeReason_ = reason;
nextNeedsChange_ = true;
}
} else {
VERBOSE_LOG(G3D, "No texture in cache, decoding...");
TexCacheEntry entryNew = { 0 };
cache[cachekey] = entryNew;

if (hasClut && clutRenderAddress_ != 0xFFFFFFFF) {
WARN_LOG_REPORT_ONCE(clutUseRender, G3D, "Using texture with rendered CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat());
}

entry = &cache[cachekey];
if (g_Config.bTextureBackoffCache) {
entry->status = TexCacheEntry::STATUS_HASHING;
} else {
entry->status = TexCacheEntry::STATUS_UNRELIABLE;
}

nextNeedsChange_ = false;
}

// We have to decode it, let's setup the cache entry first.
entry->addr = texaddr;
entry->hash = texhash;
entry->dim = dim;
entry->format = format;
entry->maxLevel = maxLevel;

// This would overestimate the size in many case so we underestimate instead
// to avoid excessive clearing caused by cache invalidations.
entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8;
entry->bufw = bufw;

entry->cluthash = cluthash;

gstate_c.curTextureWidth = w;
gstate_c.curTextureHeight = h;

// Before we go reading the texture from memory, let's check for render-to-texture.
// We must do this early so we have the right w/h.
entry->framebuffer = 0;
for (size_t i = 0, n = fbCache_.size(); i < n; ++i) {
auto framebuffer = fbCache_[i];
AttachFramebuffer(entry, framebuffer->fb_address, framebuffer);
}

// If we ended up with a framebuffer, attach it - no texture decoding needed.
if (entry->framebuffer) {
SetTextureFramebuffer(entry, entry->framebuffer);
}

nextTexture_ = entry;
nextNeedsRehash_ = entry->framebuffer == nullptr;
// We still need to rebuild, to allocate a texture. But we'll bail early.
nextNeedsRebuild_ = true;
}

// Removes old textures.
void TextureCacheCommon::Decimate() {
if (--decimationCounter_ <= 0) {
Expand Down
9 changes: 8 additions & 1 deletion GPU/Common/TextureCacheCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class TextureCacheCommon {
void LoadClut(u32 clutAddr, u32 loadBytes);
bool GetCurrentClutBuffer(GPUDebugBuffer &buffer);

void SetTexture(bool force = false);
void ApplyTexture();
bool SetOffsetTexture(u32 offset);
void Invalidate(u32 addr, int size, GPUInvalidationType type);
Expand Down Expand Up @@ -212,7 +213,7 @@ class TextureCacheCommon {
virtual void ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) = 0;
virtual bool HandleTextureChange(TexCacheEntry *const entry, const char *reason, bool initialMatch, bool doDelete) = 0;
virtual void BuildTexture(TexCacheEntry *const entry, bool replaceImages) = 0;

virtual void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) = 0;
bool CheckFullHash(TexCacheEntry *const entry, bool &doDelete);

// Separate to keep main texture cache size down.
Expand Down Expand Up @@ -262,6 +263,10 @@ class TextureCacheCommon {
}
}

static inline u32 MiniHash(const u32 *ptr) {
return ptr[0];
}

Draw::DrawContext *draw_;
TextureReplacer replacer;
FramebufferManagerCommon *framebufferManager_;
Expand Down Expand Up @@ -312,6 +317,8 @@ class TextureCacheCommon {
bool nextNeedsRehash_;
bool nextNeedsChange_;
bool nextNeedsRebuild_;

bool isBgraBackend_;
};

inline bool TexCacheEntry::Matches(u16 dim2, u8 format2, u8 maxLevel2) const {
Expand Down
Loading

0 comments on commit f15e25c

Please sign in to comment.