From bea6d787c0ba94d85f7dd71a627df244d2a6714b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 18 Mar 2017 11:42:57 -0700 Subject: [PATCH 1/5] Texcache: Cleanup some code formatting. Makes it more readable. No functional impact. --- GPU/Common/TextureCacheCommon.cpp | 74 +++++++++++++++---------------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 58ba41814d..d609ba4b1d 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1438,13 +1438,11 @@ void TextureCacheCommon::DeleteTexture(TexCache::iterator it) { } bool TextureCacheCommon::CheckFullHash(TexCacheEntry *entry, bool &doDelete) { - bool hashFail = false; int w = gstate.getTextureWidth(0); int h = gstate.getTextureHeight(0); u32 fullhash = QuickTexHash(replacer_, entry->addr, entry->bufw, w, h, GETextureFormat(entry->format), entry); - if (fullhash != entry->fullhash) { - hashFail = true; - } else { + + if (fullhash == entry->fullhash) { if (g_Config.bTextureBackoffCache) { if (entry->GetHashStatus() != TexCacheEntry::STATUS_HASHING && entry->numFrames > TexCacheEntry::FRAMES_REGAIN_TRUST) { // Reset to STATUS_HASHING. @@ -1454,51 +1452,49 @@ bool TextureCacheCommon::CheckFullHash(TexCacheEntry *entry, bool &doDelete) { } else if (entry->numFrames > TEXCACHE_FRAME_CHANGE_FREQUENT_REGAIN_TRUST) { entry->status &= ~TexCacheEntry::STATUS_CHANGE_FREQUENT; } + + return true; } - if (hashFail) { - entry->status |= TexCacheEntry::STATUS_UNRELIABLE; - if (entry->numFrames < TEXCACHE_FRAME_CHANGE_FREQUENT) { - if (entry->status & TexCacheEntry::STATUS_FREE_CHANGE) { - entry->status &= ~TexCacheEntry::STATUS_FREE_CHANGE; - } else { - entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; - } + entry->status |= TexCacheEntry::STATUS_UNRELIABLE; + if (entry->numFrames < TEXCACHE_FRAME_CHANGE_FREQUENT) { + if (entry->status & TexCacheEntry::STATUS_FREE_CHANGE) { + entry->status &= ~TexCacheEntry::STATUS_FREE_CHANGE; + } else { + entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; } - entry->numFrames = 0; + } + entry->numFrames = 0; - // Don't give up just yet. Let's try the secondary cache if it's been invalidated before. - // If it's failed a bunch of times, then the second cache is just wasting time and VRAM. - if (g_Config.bTextureSecondaryCache) { - if (entry->numInvalidated > 2 && entry->numInvalidated < 128 && !lowMemoryMode_) { - u64 secondKey = fullhash | (u64)entry->cluthash << 32; - TexCache::iterator secondIter = secondCache_.find(secondKey); - if (secondIter != secondCache_.end()) { - TexCacheEntry *secondEntry = secondIter->second.get(); - if (secondEntry->Matches(entry->dim, entry->format, entry->maxLevel)) { - // Reset the numInvalidated value lower, we got a match. - if (entry->numInvalidated > 8) { - --entry->numInvalidated; - } - nextTexture_ = secondEntry; - return true; + // Don't give up just yet. Let's try the secondary cache if it's been invalidated before. + // If it's failed a bunch of times, then the second cache is just wasting time and VRAM. + if (g_Config.bTextureSecondaryCache) { + if (entry->numInvalidated > 2 && entry->numInvalidated < 128 && !lowMemoryMode_) { + u64 secondKey = fullhash | (u64)entry->cluthash << 32; + TexCache::iterator secondIter = secondCache_.find(secondKey); + if (secondIter != secondCache_.end()) { + TexCacheEntry *secondEntry = secondIter->second.get(); + if (secondEntry->Matches(entry->dim, entry->format, entry->maxLevel)) { + // Reset the numInvalidated value lower, we got a match. + if (entry->numInvalidated > 8) { + --entry->numInvalidated; } - } else { - secondKey = entry->fullhash | ((u64)entry->cluthash << 32); - secondCacheSizeEstimate_ += EstimateTexMemoryUsage(entry); - // Is this wise? We simply copy the entry. - secondCache_[secondKey].reset(new TexCacheEntry(*entry)); - doDelete = false; + nextTexture_ = secondEntry; + return true; } + } else { + secondKey = entry->fullhash | ((u64)entry->cluthash << 32); + secondCacheSizeEstimate_ += EstimateTexMemoryUsage(entry); + // Is this wise? We simply copy the entry. + secondCache_[secondKey].reset(new TexCacheEntry(*entry)); + doDelete = false; } } - - // We know it failed, so update the full hash right away. - entry->fullhash = fullhash; - return false; } - return true; + // We know it failed, so update the full hash right away. + entry->fullhash = fullhash; + return false; } void TextureCacheCommon::Invalidate(u32 addr, int size, GPUInvalidationType type) { From 30ff5d6cdb886d10f115f89ebe2dd2c001a99d27 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 18 Mar 2017 11:44:06 -0700 Subject: [PATCH 2/5] Texcache: Apply freq change flag to minihash fail. Previously, by mistake, only full hash fails could flag a frequently changing texture - minihash checks skipped this, causing scaling performance issues. Fixes #9125. --- GPU/Common/TextureCacheCommon.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index d609ba4b1d..98a7b90956 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -349,11 +349,12 @@ void TextureCacheCommon::SetTexture(bool force) { nextTexture_ = entry; nextNeedsRehash_ = rehash; nextNeedsChange_ = false; - // Might need a rebuild if the hash fails. + // Might need a rebuild if the hash fails, but that will be set later. nextNeedsRebuild_ = false; VERBOSE_LOG(G3D, "Texture at %08x Found in Cache, applying", texaddr); return; //Done! } else { + // Wasn't a match, we will rebuild. nextChangeReason_ = reason; nextNeedsChange_ = true; } @@ -502,6 +503,16 @@ bool TextureCacheCommon::HandleTextureChange(TexCacheEntry *const entry, const c } } + entry->status |= TexCacheEntry::STATUS_UNRELIABLE; + if (entry->numFrames < TEXCACHE_FRAME_CHANGE_FREQUENT) { + if (entry->status & TexCacheEntry::STATUS_FREE_CHANGE) { + entry->status &= ~TexCacheEntry::STATUS_FREE_CHANGE; + } else { + entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; + } + } + entry->numFrames = 0; + return replaceImages; } @@ -1356,7 +1367,6 @@ void TextureCacheCommon::ReadIndexedTex(u8 *out, int outPitch, int level, const } } - void TextureCacheCommon::ApplyTexture() { TexCacheEntry *entry = nextTexture_; if (entry == nullptr) { @@ -1456,19 +1466,12 @@ bool TextureCacheCommon::CheckFullHash(TexCacheEntry *entry, bool &doDelete) { return true; } - entry->status |= TexCacheEntry::STATUS_UNRELIABLE; - if (entry->numFrames < TEXCACHE_FRAME_CHANGE_FREQUENT) { - if (entry->status & TexCacheEntry::STATUS_FREE_CHANGE) { - entry->status &= ~TexCacheEntry::STATUS_FREE_CHANGE; - } else { - entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; - } - } - entry->numFrames = 0; - // Don't give up just yet. Let's try the secondary cache if it's been invalidated before. // If it's failed a bunch of times, then the second cache is just wasting time and VRAM. if (g_Config.bTextureSecondaryCache) { + // Don't forget this one was unreliable (in case we match a secondary entry.) + entry->status |= TexCacheEntry::STATUS_UNRELIABLE; + if (entry->numInvalidated > 2 && entry->numInvalidated < 128 && !lowMemoryMode_) { u64 secondKey = fullhash | (u64)entry->cluthash << 32; TexCache::iterator secondIter = secondCache_.find(secondKey); From d0ea5ed72b904f73b629d9c2e76c93f1cab613d6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 18 Mar 2017 11:49:30 -0700 Subject: [PATCH 3/5] Texcache: Always mark videos as freq changing. This prevents scaling of the first frame in some cases, when later frames would also not be scaled. Improves performance and reduces frame mismatch. --- GPU/Common/TextureCacheCommon.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 98a7b90956..5fc44af3f0 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1378,6 +1378,13 @@ void TextureCacheCommon::ApplyTexture() { bool replaceImages = false; if (nextNeedsRebuild_) { + // Regardless of hash fails or otherwise, if this is a video, mark it frequently changing. + // This prevents temporary scaling perf hits on the first second of video. + bool isVideo = videos_.find(entry->addr & 0x3FFFFFFF) != videos_.end(); + if (isVideo) { + entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; + } + if (nextNeedsRehash_) { // Update the hash on the texture. int w = gstate.getTextureWidth(0); From 404c61d77dc00b6dc1b04f7d7362c1b0c07b0453 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 18 Mar 2017 11:51:05 -0700 Subject: [PATCH 4/5] Minor typo fixes. --- Core/Screenshot.h | 3 +-- GPU/Common/ShaderTranslation.cpp | 4 ++-- GPU/Common/TextureCacheCommon.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Core/Screenshot.h b/Core/Screenshot.h index 6514830d3a..b529fad494 100644 --- a/Core/Screenshot.h +++ b/Core/Screenshot.h @@ -28,13 +28,12 @@ enum ScreenshotType { // What's being show on screen (e.g. including FPS, etc.) SCREENSHOT_OUTPUT, // What the game rendered (e.g. at render resolution) to the display. - // Can only be used while in game. SCREENSHOT_DISPLAY, // What the game is in-progress rendering now. - // Can only be used while in game. SCREENSHOT_RENDER, }; const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u32 &h); +// Can only be used while in game. bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1); diff --git a/GPU/Common/ShaderTranslation.cpp b/GPU/Common/ShaderTranslation.cpp index 675451fca6..5a0e9027ab 100644 --- a/GPU/Common/ShaderTranslation.cpp +++ b/GPU/Common/ShaderTranslation.cpp @@ -210,7 +210,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade // Can't fail, parsing worked, "linking" worked. glslang::GlslangToSpv(*program.getIntermediate(shaderStage), spirv); - // Alright, step 1 done. Now let's takes this SPIR-V shader and output in our desired format. + // Alright, step 1 done. Now let's take this SPIR-V shader and output in our desired format. switch (destLang) { case GLSL_VULKAN: @@ -276,4 +276,4 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade } } -#endif \ No newline at end of file +#endif diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 5fc44af3f0..49b4a03981 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -34,7 +34,7 @@ #include #endif -// Videos should be updated every few frames, so we forge quickly. +// Videos should be updated every few frames, so we forget quickly. #define VIDEO_DECIMATE_AGE 4 // If a texture hasn't been seen for this many frames, get rid of it. From b21c081860d952916cae52c4fada8c993b1593a9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 18 Mar 2017 12:08:06 -0700 Subject: [PATCH 5/5] Texcache: Clarify STATUS_FREE_CHANGE usage. --- GPU/Common/TextureCacheCommon.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 9cb2d04d0f..bcca4c19f5 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -104,6 +104,9 @@ struct TexCacheEntry { STATUS_DEPALETTIZE = 0x40, // Needs to go through a depalettize pass. STATUS_TO_SCALE = 0x80, // Pending texture scaling in a later frame. STATUS_IS_SCALED = 0x100, // Has been scaled (can't be replaceImages'd.) + // When hashing large textures, we optimize 512x512 down to 512x272 by default, since this + // is commonly the only part accessed. If access is made above 272, we hash the entire + // texture, and set this flag to allow scaling the texture just once for the new hash. STATUS_FREE_CHANGE = 0x200, // Allow one change before marking "frequent". };