From bf39e61458e5dce539181e59ae9f247c566c4bd2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 13:44:31 -0700 Subject: [PATCH 01/34] Setup initial structure for texture replacements. --- CMakeLists.txt | 2 + GPU/Common/TextureCacheCommon.cpp | 4 + GPU/Common/TextureCacheCommon.h | 5 +- GPU/Common/TextureReplacer.cpp | 47 ++++++++++ GPU/Common/TextureReplacer.h | 105 +++++++++++++++++++++++ GPU/Directx9/TextureCacheDX9.cpp | 105 ++++++++++++++++++----- GPU/Directx9/TextureCacheDX9.h | 2 +- GPU/GLES/TextureCache.cpp | 137 ++++++++++++++++++++++-------- GPU/GLES/TextureCache.h | 2 +- GPU/GPU.vcxproj | 2 + GPU/GPU.vcxproj.filters | 6 ++ GPU/Vulkan/TextureCacheVulkan.cpp | 69 +++++++++++++-- Qt/GPU.pro | 1 + android/jni/Android.mk | 1 + 14 files changed, 423 insertions(+), 65 deletions(-) create mode 100644 GPU/Common/TextureReplacer.cpp create mode 100644 GPU/Common/TextureReplacer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d78af8512a0..5986403a05dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1491,6 +1491,8 @@ add_library(GPU OBJECT GPU/Common/TextureDecoder.h GPU/Common/TextureCacheCommon.cpp GPU/Common/TextureCacheCommon.h + GPU/Common/TextureReplacer.cpp + GPU/Common/TextureReplacer.h GPU/Common/TextureScalerCommon.cpp GPU/Common/TextureScalerCommon.h ${GPU_NEON} diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 0c607e7a4353..67345cd33a79 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -51,6 +51,8 @@ TextureCacheCommon::TextureCacheCommon() tmpTexBuf32.resize(512 * 512); // 1MB tmpTexBuf16.resize(512 * 512); // 0.5MB tmpTexBufRearrange.resize(512 * 512); // 1MB + + replacer.Init(); } TextureCacheCommon::~TextureCacheCommon() { @@ -287,6 +289,8 @@ void TextureCacheCommon::NotifyConfigChanged() { } standardScaleFactor_ = scaleFactor; + + replacer.NotifyConfigChanged(); } void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index d2af36993eb2..17da5fbf76db 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -21,8 +21,9 @@ #include #include "Common/CommonTypes.h" -#include "GPU/Common/GPUDebugInterface.h" #include "Common/MemoryUtil.h" +#include "GPU/Common/GPUDebugInterface.h" +#include "GPU/Common/TextureReplacer.h" enum TextureFiltering { TEX_FILTER_AUTO = 1, @@ -154,6 +155,8 @@ class TextureCacheCommon { virtual void DownloadFramebufferForClut(u32 clutAddr, u32 bytes) = 0; + TextureReplacer replacer; + TexCache cache; u32 cacheSizeEstimate_; diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp new file mode 100644 index 000000000000..7a8fdd2997c8 --- /dev/null +++ b/GPU/Common/TextureReplacer.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2016- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "GPU/Common/TextureReplacer.h" + +TextureReplacer::TextureReplacer() : enabled_(false) { +} + +TextureReplacer::~TextureReplacer() { +} + + +void TextureReplacer::Init() { +} + +void TextureReplacer::NotifyConfigChanged() { +} + +u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV) { + return 0; +} + +ReplacedTexture TextureReplacer::FindReplacement(u32 hash) { + ReplacedTexture result; + result.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; + return result; +} + +void TextureReplacer::NotifyTextureDecoded(u32 hash, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt) { +} + +void ReplacedTexture::Load(int level, void *out, int rowPitch) { +} diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h new file mode 100644 index 000000000000..b7a3f1e8b04d --- /dev/null +++ b/GPU/Common/TextureReplacer.h @@ -0,0 +1,105 @@ +// Copyright (c) 2016- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include "Common/Common.h" +#include "GPU/ge_constants.h" + +class TextureCacheCommon; +class TextureReplacer; + +enum class ReplacedTextureFormat { + F_5650, + F_5551, + F_4444, + F_8888, +}; + +// These must match the constants in TextureCacheCommon. +enum class ReplacedTextureAlpha { + UNKNOWN = 0x04, + FULL = 0x00, + SIMPLE = 0x08, +}; + +struct ReplacedTexureLevel { + int w; + int h; + ReplacedTextureFormat fmt; +}; + +struct ReplacedTexture { + inline bool Valid() { + return !levels_.empty(); + } + + bool GetSize(int level, int &w, int &h) { + if ((size_t)level < levels_.size()) { + w = levels_[level].w; + h = levels_[level].h; + return true; + } + return false; + } + + int MaxLevel() { + return (int)levels_.size() - 1; + } + + ReplacedTextureFormat Format(int level) { + if ((size_t)level < levels_.size()) { + return levels_[level].fmt; + } + return ReplacedTextureFormat::F_8888; + } + + u8 AlphaStatus() { + return (u8)alphaStatus_; + } + + void Load(int level, void *out, int rowPitch); + +protected: + std::vector levels_; + ReplacedTextureAlpha alphaStatus_; + + friend TextureReplacer; +}; + +class TextureReplacer { +public: + TextureReplacer(); + ~TextureReplacer(); + + void Init(); + void NotifyConfigChanged(); + + inline bool Enabled() { + return enabled_; + } + + u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV); + + ReplacedTexture FindReplacement(u32 hash); + + void NotifyTextureDecoded(u32 hash, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt); + +protected: + bool enabled_; +}; diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 37c6e833fa83..31bd902e83fb 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -585,7 +585,11 @@ static inline u32 MiniHash(const u32 *ptr) { return ptr[0]; } -static inline u32 QuickTexHash(u32 addr, int bufw, int w, int h, GETextureFormat format, TextureCacheDX9::TexCacheEntry *entry) { +static inline u32 QuickTexHash(TextureReplacer &replacer, u32 addr, int bufw, int w, int h, GETextureFormat format, TextureCacheDX9::TexCacheEntry *entry) { + if (replacer.Enabled()) { + return replacer.ComputeHash(addr, bufw, w, h, format, entry->maxSeenV); + } + if (h == 512 && entry->maxSeenV < 512 && entry->maxSeenV != 0) { h = (int)entry->maxSeenV; } @@ -1035,13 +1039,13 @@ void TextureCacheDX9::SetTexture(bool force) { bool hashFail = false; if (texhash != entry->hash) { - fullhash = QuickTexHash(texaddr, bufw, w, h, format, entry); + fullhash = QuickTexHash(replacer, texaddr, bufw, w, h, format, entry); hashFail = true; rehash = false; } if (rehash && entry->GetHashStatus() != TexCacheEntry::STATUS_RELIABLE) { - fullhash = QuickTexHash(texaddr, bufw, w, h, format, entry); + fullhash = QuickTexHash(replacer, texaddr, bufw, w, h, format, entry); if (fullhash != entry->fullhash) { hashFail = true; } else { @@ -1188,7 +1192,7 @@ void TextureCacheDX9::SetTexture(bool force) { // to avoid excessive clearing caused by cache invalidations. entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8; - entry->fullhash = fullhash == 0 ? QuickTexHash(texaddr, bufw, w, h, format, entry) : fullhash; + entry->fullhash = fullhash == 0 ? QuickTexHash(replacer, texaddr, bufw, w, h, format, entry) : fullhash; entry->cluthash = cluthash; entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK; @@ -1251,6 +1255,16 @@ void TextureCacheDX9::SetTexture(bool force) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } + ReplacedTexture replaced = replacer.FindReplacement(entry->fullhash); + if (replaced.GetSize(0, w, h)) { + // We're replacing, so we won't scale. + scaleFactor = 1; + if (g_Config.bMipMap) { + maxLevel = replaced.MaxLevel(); + badMipSizes = false; + } + } + // Don't scale the PPGe texture. if (entry->addr > 0x05000000 && entry->addr < 0x08800000) scaleFactor = 1; @@ -1279,7 +1293,7 @@ void TextureCacheDX9::SetTexture(bool force) { maxLevel = 0; } - LoadTextureLevel(*entry, 0, maxLevel, replaceImages, scaleFactor, dstFmt); + LoadTextureLevel(*entry, replaced, 0, maxLevel, replaceImages, scaleFactor, dstFmt); LPDIRECT3DTEXTURE9 &texture = DxTex(entry); if (!texture) { return; @@ -1288,10 +1302,14 @@ void TextureCacheDX9::SetTexture(bool force) { // Mipmapping is only enabled when texture scaling is disabled. if (maxLevel > 0 && scaleFactor == 1) { for (u32 i = 1; i <= maxLevel; i++) { - LoadTextureLevel(*entry, i, maxLevel, replaceImages, scaleFactor, dstFmt); + LoadTextureLevel(*entry, replaced, i, maxLevel, replaceImages, scaleFactor, dstFmt); } } + if (replaced.Valid()) { + entry->SetAlphaStatus(TexCacheEntry::Status(replaced.AlphaStatus())); + } + gstate_c.textureFullAlpha = entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL; gstate_c.textureSimpleAlpha = entry->GetAlphaStatus() != TexCacheEntry::STATUS_ALPHA_UNKNOWN; @@ -1590,31 +1608,74 @@ static inline void copyTexture(int xoffset, int yoffset, int w, int h, int pitch } } -void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, int level, int maxLevel, bool replaceImages, int scaleFactor, u32 dstFmt) { - // TODO: only do this once - u32 texByteAlign = 1; +ReplacedTextureFormat FromD3D9Format(u32 fmt) { + switch (fmt) { + case D3DFMT_R5G6B5: + return ReplacedTextureFormat::F_5650; + case D3DFMT_A1R5G5B5: + return ReplacedTextureFormat::F_5551; + case D3DFMT_A4R4G4B4: + return ReplacedTextureFormat::F_4444; + case D3DFMT_A8R8G8B8: + default: + return ReplacedTextureFormat::F_8888; + } +} - GEPaletteFormat clutformat = gstate.getClutPaletteFormat(); - int bufw; - void *finalBuf = DecodeTextureLevel(GETextureFormat(entry.format), clutformat, level, texByteAlign, dstFmt, &bufw); - if (finalBuf == NULL) { - return; +u32 ToD3D9Format(ReplacedTextureFormat fmt) { + switch (fmt) { + case ReplacedTextureFormat::F_5650: + return D3DFMT_R5G6B5; + case ReplacedTextureFormat::F_5551: + return D3DFMT_A1R5G5B5; + case ReplacedTextureFormat::F_4444: + return D3DFMT_A4R4G4B4; + case ReplacedTextureFormat::F_8888: + default: + return D3DFMT_A8R8G8B8; } +} + +void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int maxLevel, bool replaceImages, int scaleFactor, u32 dstFmt) { + // TODO: only do this once + u32 texByteAlign = 1; int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); + int bufw; + u32 *pixelData; gpuStats.numTexturesDecoded++; + if (replaced.GetSize(level, w, h)) { + tmpTexBufRearrange.resize(w * h); + int bpp = replaced.Format(level) == ReplacedTextureFormat::F_8888 ? 4 : 2; + bufw = w; + replaced.Load(level, tmpTexBufRearrange.data(), bpp * w); + pixelData = tmpTexBufRearrange.data(); + + dstFmt = ToD3D9Format(replaced.Format(level)); + } else { + GEPaletteFormat clutformat = gstate.getClutPaletteFormat(); + void *finalBuf = DecodeTextureLevel(GETextureFormat(entry.format), clutformat, level, texByteAlign, dstFmt, &bufw); + if (finalBuf == NULL) { + return; + } - u32 *pixelData = (u32 *)finalBuf; - if (scaleFactor > 1 && (entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) - scaler.Scale(pixelData, dstFmt, w, h, scaleFactor); + pixelData = (u32 *)finalBuf; + if (scaleFactor > 1 && (entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) + scaler.Scale(pixelData, dstFmt, w, h, scaleFactor); - if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { - TexCacheEntry::Status alphaStatus = CheckAlpha(pixelData, dstFmt, w, w, h); - entry.SetAlphaStatus(alphaStatus, level); - } else { - entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); + if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { + TexCacheEntry::Status alphaStatus = CheckAlpha(pixelData, dstFmt, w, w, h); + entry.SetAlphaStatus(alphaStatus, level); + } else { + entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); + } + + if (replacer.Enabled()) { + int bpp = dstFmt == D3DFMT_A8R8G8B8 ? 4 : 2; + replacer.NotifyTextureDecoded(entry.fullhash, pixelData, w * bpp, w, h, FromD3D9Format(dstFmt)); + } } LPDIRECT3DTEXTURE9 &texture = DxTex(&entry); diff --git a/GPU/Directx9/TextureCacheDX9.h b/GPU/Directx9/TextureCacheDX9.h index fea05abbdad1..ff9db0fa2077 100644 --- a/GPU/Directx9/TextureCacheDX9.h +++ b/GPU/Directx9/TextureCacheDX9.h @@ -80,7 +80,7 @@ class TextureCacheDX9 : public TextureCacheCommon { void DeleteTexture(TexCache::iterator it); void *ReadIndexedTex(int level, const u8 *texptr, int bytesPerIndex, u32 dstFmt, int bufw); void UpdateSamplingParams(TexCacheEntry &entry, bool force); - void LoadTextureLevel(TexCacheEntry &entry, int level, int maxLevel, bool replaceImages, int scaleFactor, u32 dstFmt); + void LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int maxLevel, bool replaceImages, int scaleFactor, u32 dstFmt); D3DFORMAT GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const; void *DecodeTextureLevel(GETextureFormat format, GEPaletteFormat clutformat, int level, u32 &texByteAlign, u32 &dstFmt, int *bufw = 0); TexCacheEntry::Status CheckAlpha(const u32 *pixelData, u32 dstFmt, int stride, int w, int h); diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 50bee2cd62de..d5a9599912c2 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -606,7 +606,11 @@ static inline u32 MiniHash(const u32 *ptr) { return ptr[0]; } -static inline u32 QuickTexHash(u32 addr, int bufw, int w, int h, GETextureFormat format, TextureCache::TexCacheEntry *entry) { +static inline u32 QuickTexHash(TextureReplacer &replacer, u32 addr, int bufw, int w, int h, GETextureFormat format, TextureCache::TexCacheEntry *entry) { + if (replacer.Enabled()) { + return replacer.ComputeHash(addr, bufw, w, h, format, entry->maxSeenV); + } + if (h == 512 && entry->maxSeenV < 512 && entry->maxSeenV != 0) { h = (int)entry->maxSeenV; } @@ -989,6 +993,35 @@ bool TextureCache::SetOffsetTexture(u32 offset) { return false; } +ReplacedTextureFormat FromGLESFormat(GLenum fmt) { + // TODO: 16-bit formats are incorrect, since swizzled. + switch (fmt) { + case GL_UNSIGNED_SHORT_5_6_5: + return ReplacedTextureFormat::F_5650; + case GL_UNSIGNED_SHORT_5_5_5_1: + return ReplacedTextureFormat::F_5551; + case GL_UNSIGNED_SHORT_4_4_4_4: + return ReplacedTextureFormat::F_4444; + case GL_UNSIGNED_BYTE: + default: + return ReplacedTextureFormat::F_8888; + } +} + +GLenum ToGLESFormat(ReplacedTextureFormat fmt) { + switch (fmt) { + case ReplacedTextureFormat::F_5650: + return GL_UNSIGNED_SHORT_5_6_5; + case ReplacedTextureFormat::F_5551: + return GL_UNSIGNED_SHORT_5_5_5_1; + case ReplacedTextureFormat::F_4444: + return GL_UNSIGNED_SHORT_4_4_4_4; + case ReplacedTextureFormat::F_8888: + default: + return GL_UNSIGNED_BYTE; + } +} + void TextureCache::SetTexture(bool force) { #ifdef DEBUG_TEXTURES if (SetDebugTexture()) { @@ -1111,13 +1144,13 @@ void TextureCache::SetTexture(bool force) { bool hashFail = false; if (texhash != entry->hash) { - fullhash = QuickTexHash(texaddr, bufw, w, h, format, entry); + fullhash = QuickTexHash(replacer, texaddr, bufw, w, h, format, entry); hashFail = true; rehash = false; } if (rehash && entry->GetHashStatus() != TexCacheEntry::STATUS_RELIABLE) { - fullhash = QuickTexHash(texaddr, bufw, w, h, format, entry); + fullhash = QuickTexHash(replacer, texaddr, bufw, w, h, format, entry); if (fullhash != entry->fullhash) { hashFail = true; } else { @@ -1264,7 +1297,7 @@ void TextureCache::SetTexture(bool force) { // to avoid excessive clearing caused by cache invalidations. entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8; - entry->fullhash = fullhash == 0 ? QuickTexHash(texaddr, bufw, w, h, format, entry) : fullhash; + entry->fullhash = fullhash == 0 ? QuickTexHash(replacer, texaddr, bufw, w, h, format, entry) : fullhash; entry->cluthash = cluthash; entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK; @@ -1331,6 +1364,16 @@ void TextureCache::SetTexture(bool force) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } + ReplacedTexture replaced = replacer.FindReplacement(entry->fullhash); + if (replaced.GetSize(0, w, h)) { + // We're replacing, so we won't scale. + scaleFactor = 1; + if (g_Config.bMipMap) { + maxLevel = replaced.MaxLevel(); + badMipSizes = false; + } + } + // Don't scale the PPGe texture. if (entry->addr > 0x05000000 && entry->addr < 0x08800000) scaleFactor = 1; @@ -1355,14 +1398,15 @@ void TextureCache::SetTexture(bool force) { // glTexStorage2D probably has few benefits for us anyway. if (false && gl_extensions.GLES3 && maxLevel > 0) { // glTexStorage2D requires the use of sized formats. + GLenum actualFmt = replaced.Valid() ? ToGLESFormat(replaced.Format(0)) : dstFmt; GLenum storageFmt = GL_RGBA8; - switch (dstFmt) { + switch (actualFmt) { case GL_UNSIGNED_BYTE: storageFmt = GL_RGBA8; break; case GL_UNSIGNED_SHORT_5_6_5: storageFmt = GL_RGB565; break; case GL_UNSIGNED_SHORT_4_4_4_4: storageFmt = GL_RGBA4; break; case GL_UNSIGNED_SHORT_5_5_5_1: storageFmt = GL_RGB5_A1; break; default: - ERROR_LOG(G3D, "Unknown dstfmt %i", (int)dstFmt); + ERROR_LOG(G3D, "Unknown dstfmt %i", (int)actualFmt); break; } // TODO: This may cause bugs, since it hard-sets the texture w/h, and we might try to reuse it later with a different size. @@ -1377,7 +1421,7 @@ void TextureCache::SetTexture(bool force) { // be as good quality as the game's own (might even be better in some cases though). // Always load base level texture here - LoadTextureLevel(*entry, 0, replaceImages, scaleFactor, dstFmt); + LoadTextureLevel(*entry, replaced, 0, replaceImages, scaleFactor, dstFmt); // Mipmapping only enable when texture scaling disable if (maxLevel > 0 && scaleFactor == 1) { @@ -1387,7 +1431,7 @@ void TextureCache::SetTexture(bool force) { glGenerateMipmap(GL_TEXTURE_2D); } else { for (int i = 1; i <= maxLevel; i++) { - LoadTextureLevel(*entry, i, replaceImages, scaleFactor, dstFmt); + LoadTextureLevel(*entry, replaced, i, replaceImages, scaleFactor, dstFmt); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxLevel); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)maxLevel); @@ -1405,6 +1449,10 @@ void TextureCache::SetTexture(bool force) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); } + if (replaced.Valid()) { + entry->SetAlphaStatus(TexCacheEntry::Status(replaced.AlphaStatus())); + } + if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY)) { int aniso = 1 << g_Config.iAnisotropyLevel; float anisotropyLevel = (float) aniso > maxAnisotropyLevel ? maxAnisotropyLevel : (float) aniso; @@ -1709,50 +1757,69 @@ TextureCache::TexCacheEntry::Status TextureCache::CheckAlpha(const u32 *pixelDat return (TexCacheEntry::Status)res; } -void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level, bool replaceImages, int scaleFactor, GLenum dstFmt) { +void TextureCache::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, bool replaceImages, int scaleFactor, GLenum dstFmt) { int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); bool useUnpack = false; bool useBGRA; u32 *pixelData; - { - - PROFILE_THIS_SCOPE("decodetex"); // TODO: only do this once u32 texByteAlign = 1; - GEPaletteFormat clutformat = gstate.getClutPaletteFormat(); - int bufw; - void *finalBuf = DecodeTextureLevel(GETextureFormat(entry.format), clutformat, level, texByteAlign, dstFmt, scaleFactor, &bufw); - if (finalBuf == NULL) { - return; - } - gpuStats.numTexturesDecoded++; - // Can restore these and remove the fixup at the end of DecodeTextureLevel on desktop GL and GLES 3. - if (scaleFactor == 1 && gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE) && w != bufw) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, bufw); - useUnpack = true; - } - - glPixelStorei(GL_UNPACK_ALIGNMENT, texByteAlign); + if (replaced.Valid()) { + PROFILE_THIS_SCOPE("replacetex"); - useBGRA = UseBGRA8888() && dstFmt == GL_UNSIGNED_BYTE; + tmpTexBufRearrange.resize(w * h); + int bpp = replaced.Format(level) == ReplacedTextureFormat::F_8888 ? 4 : 2; + replaced.Load(level, tmpTexBufRearrange.data(), bpp * w); + pixelData = tmpTexBufRearrange.data(); - pixelData = (u32 *)finalBuf; - if (scaleFactor > 1) - scaler.Scale(pixelData, dstFmt, w, h, scaleFactor); + dstFmt = ToGLESFormat(replaced.Format(level)); - if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { - TexCacheEntry::Status alphaStatus = CheckAlpha(pixelData, dstFmt, useUnpack ? bufw : w, w, h); - entry.SetAlphaStatus(alphaStatus, level); + texByteAlign = bpp; + useBGRA = false; } else { - entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); - } + + PROFILE_THIS_SCOPE("decodetex"); + + GEPaletteFormat clutformat = gstate.getClutPaletteFormat(); + int bufw; + void *finalBuf = DecodeTextureLevel(GETextureFormat(entry.format), clutformat, level, texByteAlign, dstFmt, scaleFactor, &bufw); + if (finalBuf == NULL) { + return; + } + + // Can restore these and remove the fixup at the end of DecodeTextureLevel on desktop GL and GLES 3. + if (scaleFactor == 1 && gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE) && w != bufw) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, bufw); + useUnpack = true; + } + + useBGRA = UseBGRA8888() && dstFmt == GL_UNSIGNED_BYTE; + + pixelData = (u32 *)finalBuf; + if (scaleFactor > 1) + scaler.Scale(pixelData, dstFmt, w, h, scaleFactor); + + if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { + TexCacheEntry::Status alphaStatus = CheckAlpha(pixelData, dstFmt, useUnpack ? bufw : w, w, h); + entry.SetAlphaStatus(alphaStatus, level); + } else { + entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); + } + + if (replacer.Enabled()) { + int bpp = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; + // TODO: BGRA. + replacer.NotifyTextureDecoded(entry.fullhash, pixelData, w * bpp, w, h, FromGLESFormat(dstFmt)); + } } + glPixelStorei(GL_UNPACK_ALIGNMENT, texByteAlign); + GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; GLuint components2 = components; diff --git a/GPU/GLES/TextureCache.h b/GPU/GLES/TextureCache.h index 946ce0539446..10cddc6dcb7b 100644 --- a/GPU/GLES/TextureCache.h +++ b/GPU/GLES/TextureCache.h @@ -95,7 +95,7 @@ class TextureCache : public TextureCacheCommon { void DeleteTexture(TexCache::iterator it); void *ReadIndexedTex(int level, const u8 *texptr, int bytesPerIndex, GLuint dstFmt, int bufw); void UpdateSamplingParams(TexCacheEntry &entry, bool force); - void LoadTextureLevel(TexCacheEntry &entry, int level, bool replaceImages, int scaleFactor, GLenum dstFmt); + void LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, bool replaceImages, int scaleFactor, GLenum dstFmt); GLenum GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const; void *DecodeTextureLevel(GETextureFormat format, GEPaletteFormat clutformat, int level, u32 &texByteAlign, GLenum dstFmt, int scaleFactor, int *bufw = 0); TexCacheEntry::Status CheckAlpha(const u32 *pixelData, GLenum dstFmt, int stride, int w, int h); diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 5b49067da8b8..1c6733df4255 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -199,6 +199,7 @@ true + @@ -275,6 +276,7 @@ true + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 9818d21e9299..2ebdc392476e 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -240,6 +240,9 @@ Vulkan + + Common + @@ -461,5 +464,8 @@ Vulkan + + Common + diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 74c42fec1d2b..82d9a93f1f88 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -591,7 +591,11 @@ static inline u32 MiniHash(const u32 *ptr) { return ptr[0]; } -static inline u32 QuickTexHash(u32 addr, int bufw, int w, int h, GETextureFormat format, TextureCacheVulkan::TexCacheEntry *entry) { +static inline u32 QuickTexHash(TextureReplacer &replacer, u32 addr, int bufw, int w, int h, GETextureFormat format, TextureCacheVulkan::TexCacheEntry *entry) { + if (replacer.Enabled()) { + return replacer.ComputeHash(addr, bufw, w, h, format, entry->maxSeenV); + } + if (h == 512 && entry->maxSeenV < 512 && entry->maxSeenV != 0) { h = (int)entry->maxSeenV; } @@ -874,6 +878,34 @@ bool TextureCacheVulkan::SetOffsetTexture(u32 offset) { return false; } +ReplacedTextureFormat FromVulkanFormat(VkFormat fmt) { + switch (fmt) { + case VULKAN_565_FORMAT: + return ReplacedTextureFormat::F_5650; + case VULKAN_1555_FORMAT: + return ReplacedTextureFormat::F_5551; + case VULKAN_4444_FORMAT: + return ReplacedTextureFormat::F_4444; + case VULKAN_8888_FORMAT: + default: + return ReplacedTextureFormat::F_8888; + } +} + +VkFormat ToVulkanFormat(ReplacedTextureFormat fmt) { + switch (fmt) { + case ReplacedTextureFormat::F_5650: + return VULKAN_565_FORMAT; + case ReplacedTextureFormat::F_5551: + return VULKAN_1555_FORMAT; + case ReplacedTextureFormat::F_4444: + return VULKAN_4444_FORMAT; + case ReplacedTextureFormat::F_8888: + default: + return VULKAN_8888_FORMAT; + } +} + void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { #ifdef DEBUG_TEXTURES if (SetDebugTexture()) { @@ -993,13 +1025,13 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { bool hashFail = false; if (texhash != entry->hash) { - fullhash = QuickTexHash(texaddr, bufw, w, h, format, entry); + fullhash = QuickTexHash(replacer, texaddr, bufw, w, h, format, entry); hashFail = true; rehash = false; } if (rehash && entry->GetHashStatus() != TexCacheEntry::STATUS_RELIABLE) { - fullhash = QuickTexHash(texaddr, bufw, w, h, format, entry); + fullhash = QuickTexHash(replacer, texaddr, bufw, w, h, format, entry); if (fullhash != entry->fullhash) { hashFail = true; } else { @@ -1142,7 +1174,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { // to avoid excessive clearing caused by cache invalidations. entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8; - entry->fullhash = fullhash == 0 ? QuickTexHash(texaddr, bufw, w, h, format, entry) : fullhash; + entry->fullhash = fullhash == 0 ? QuickTexHash(replacer, texaddr, bufw, w, h, format, entry) : fullhash; entry->cluthash = cluthash; entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK; @@ -1205,6 +1237,15 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } + ReplacedTexture replaced = replacer.FindReplacement(entry->fullhash); + if (replaced.GetSize(0, w, h)) { + // We're replacing, so we won't scale. + scaleFactor = 1; + if (g_Config.bMipMap) { + maxLevel = replaced.MaxLevel(); + } + } + // Don't scale the PPGe texture. if (entry->addr > 0x05000000 && entry->addr < 0x08800000) scaleFactor = 1; @@ -1230,6 +1271,9 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { } VkFormat actualFmt = scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt; + if (replaced.Valid()) { + actualFmt = ToVulkanFormat(replaced.Format(0)); + } if (!entry->vkTex) { entry->vkTex = new CachedTextureVulkan(); entry->vkTex->texture_ = new VulkanTexture(vulkan_, allocator_); @@ -1290,15 +1334,29 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { for (int i = 0; i <= maxLevel; i++) { int mipWidth = gstate.getTextureWidth(i) * scaleFactor; int mipHeight = gstate.getTextureHeight(i) * scaleFactor; + if (replaced.Valid()) { + replaced.GetSize(i, mipWidth, mipHeight); + } int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; int stride = (mipWidth * bpp + 15) & ~15; int size = stride * mipHeight; uint32_t bufferOffset; VkBuffer texBuf; void *data = uploadBuffer->Push(size, &bufferOffset, &texBuf); - LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); + if (replaced.Valid()) { + replaced.Load(i, data, stride); + } else { + LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); + if (replacer.Enabled()) { + replacer.NotifyTextureDecoded(entry->fullhash, data, stride, mipWidth, mipHeight, FromVulkanFormat(actualFmt)); + } + } entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); } + + if (replaced.Valid()) { + entry->SetAlphaStatus(TexCacheEntry::Status(replaced.AlphaStatus())); + } } entry->vkTex->texture_->EndCreate(); @@ -1581,6 +1639,7 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt } if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { + // TODO: bufw may be wrong. TexCacheEntry::Status alphaStatus = CheckAlpha(pixelData, dstFmt, bufw, w, h); entry.SetAlphaStatus(alphaStatus, level); } else { diff --git a/Qt/GPU.pro b/Qt/GPU.pro index e993b64b5a54..ba4f607bf3b7 100644 --- a/Qt/GPU.pro +++ b/Qt/GPU.pro @@ -54,6 +54,7 @@ SOURCES += $$P/GPU/GeDisasm.cpp \ # GPU $$P/GPU/Common/TextureScalerCommon.cpp \ $$P/GPU/Common/VertexDecoderCommon.cpp \ $$P/GPU/Common/TextureCacheCommon.cpp \ + $$P/GPU/Common/TextureReplacer.cpp \ $$P/GPU/Common/TransformCommon.cpp \ $$P/GPU/Common/SoftwareTransformCommon.cpp \ $$P/GPU/Common/PostShader.cpp \ diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 6eb9e787c883..8f00efe94c29 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -207,6 +207,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/SoftwareTransformCommon.cpp.arm \ $(SRC)/GPU/Common/VertexDecoderCommon.cpp.arm \ $(SRC)/GPU/Common/TextureCacheCommon.cpp.arm \ + $(SRC)/GPU/Common/TextureReplacer.cpp \ $(SRC)/GPU/Common/TextureScalerCommon.cpp.arm \ $(SRC)/GPU/Common/SplineCommon.cpp.arm \ $(SRC)/GPU/Common/DrawEngineCommon.cpp.arm \ From c4e98433b88ee5d575404565e1fc1302c27d806e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 14:05:03 -0700 Subject: [PATCH 02/34] Add config to save or load replaced textures. --- Core/Config.cpp | 3 ++- Core/Config.h | 2 ++ Core/System.cpp | 2 ++ Core/System.h | 1 + GPU/Common/TextureReplacer.cpp | 40 +++++++++++++++++++++++++++++++++- GPU/Common/TextureReplacer.h | 3 +++ 6 files changed, 49 insertions(+), 2 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index e66ac808c6ff..a5b4bb79d557 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -477,8 +477,9 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("ImmersiveMode", &g_Config.bImmersiveMode, false, true, true), ReportedConfigSetting("TrueColor", &g_Config.bTrueColor, true, true, true), - ReportedConfigSetting("MipMap", &g_Config.bMipMap, true, true, true), + ReportedConfigSetting("ReplaceTextures", &g_Config.bReplaceTextures, true, true, true), + ReportedConfigSetting("SaveNewTextures", &g_Config.bSaveNewTextures, false, true, true), ReportedConfigSetting("TexScalingLevel", &g_Config.iTexScalingLevel, 1, true, true), ReportedConfigSetting("TexScalingType", &g_Config.iTexScalingType, 0, true, true), diff --git a/Core/Config.h b/Core/Config.h index 8618adaa461a..b2cfc1351e45 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -180,6 +180,8 @@ struct Config { int bHighQualityDepth; bool bTrueColor; bool bMipMap; + bool bReplaceTextures; + bool bSaveNewTextures; int iTexScalingLevel; // 1 = off, 2 = 2x, ..., 5 = 5x int iTexScalingType; // 0 = xBRZ, 1 = Hybrid bool bTexDeposterize; diff --git a/Core/System.cpp b/Core/System.cpp index d1613cc61742..5a78541596d7 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -586,6 +586,8 @@ std::string GetSysDirectory(PSPDirectories directoryType) { return g_Config.memStickDirectory + "PSP/PPSSPP_STATE/"; case DIRECTORY_CACHE: return g_Config.memStickDirectory + "PSP/SYSTEM/CACHE/"; + case DIRECTORY_TEXTURES: + return g_Config.memStickDirectory + "PSP/TEXTURES/"; case DIRECTORY_APP_CACHE: if (!g_Config.appCacheDirectory.empty()) { return g_Config.appCacheDirectory; diff --git a/Core/System.h b/Core/System.h index 4ff1767d8781..7815b60accc1 100644 --- a/Core/System.h +++ b/Core/System.h @@ -45,6 +45,7 @@ enum PSPDirectories { DIRECTORY_DUMP, DIRECTORY_SAVESTATE, DIRECTORY_CACHE, + DIRECTORY_TEXTURES, DIRECTORY_APP_CACHE, // Use the OS app cache if available }; diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 7a8fdd2997c8..423780d32e0c 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -15,6 +15,10 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Common/FileUtil.h" +#include "Core/Config.h" +#include "Core/System.h" +#include "Core/ELF/ParamSFO.h" #include "GPU/Common/TextureReplacer.h" TextureReplacer::TextureReplacer() : enabled_(false) { @@ -23,25 +27,59 @@ TextureReplacer::TextureReplacer() : enabled_(false) { TextureReplacer::~TextureReplacer() { } - void TextureReplacer::Init() { + NotifyConfigChanged(); } void TextureReplacer::NotifyConfigChanged() { + gameID_ = g_paramSFO.GetValueString("DISC_ID"); + + enabled_ = !gameID_.empty() && (g_Config.bReplaceTextures || g_Config.bSaveNewTextures); + if (enabled_) { + basePath_ = GetSysDirectory(DIRECTORY_TEXTURES) + gameID_ + "/"; + + // If we're saving, auto-create the directory. + if (g_Config.bSaveNewTextures && !File::Exists(basePath_)) { + File::CreateFullPath(basePath_); + } + + enabled_ = File::Exists(basePath_) && File::IsDirectory(basePath_); + } + + // TODO: Load ini file. } u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV) { + _dbg_assert_msg_(G3D, enabled_, "Replacement not enabled"); return 0; } ReplacedTexture TextureReplacer::FindReplacement(u32 hash) { + _assert_msg_(G3D, enabled_, "Replacement not enabled"); + ReplacedTexture result; result.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; + + // Only actually replace if we're replacing. We might just be saving. + if (g_Config.bReplaceTextures) { + // TODO + } return result; } void TextureReplacer::NotifyTextureDecoded(u32 hash, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt) { + _assert_msg_(G3D, enabled_, "Replacement not enabled"); + if (!g_Config.bSaveNewTextures) { + // Ignore. + return; + } + + // TODO } void ReplacedTexture::Load(int level, void *out, int rowPitch) { + _assert_msg_(G3D, (size_t)level < levels_.size(), "Invalid miplevel"); + _assert_msg_(G3D, out != nullptr && rowPitch > 0, "Invalid out/pitch"); + + // TODO } diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h index b7a3f1e8b04d..78ccc9717597 100644 --- a/GPU/Common/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -42,6 +42,7 @@ struct ReplacedTexureLevel { int w; int h; ReplacedTextureFormat fmt; + std::string file; }; struct ReplacedTexture { @@ -102,4 +103,6 @@ class TextureReplacer { protected: bool enabled_; + std::string gameID_; + std::string basePath_; }; From cf53948cf6ee5cc570b36713f290c936fac7e960 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 14:19:23 -0700 Subject: [PATCH 03/34] Implement some initial hashing so it's not broken. --- GPU/Common/TextureReplacer.cpp | 21 ++++++++++++++++++++- GPU/Common/TextureReplacer.h | 2 ++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 423780d32e0c..67c1beff6e9d 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -15,10 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "ext/xxhash.h" #include "Common/FileUtil.h" #include "Core/Config.h" #include "Core/System.h" #include "Core/ELF/ParamSFO.h" +#include "GPU/Common/TextureDecoder.h" #include "GPU/Common/TextureReplacer.h" TextureReplacer::TextureReplacer() : enabled_(false) { @@ -51,7 +53,19 @@ void TextureReplacer::NotifyConfigChanged() { u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV) { _dbg_assert_msg_(G3D, enabled_, "Replacement not enabled"); - return 0; + + if (!LookupHashRange(addr, w, h)) { + // There wasn't any hash range, let's fall back to maxSeenV logic. + if (h == 512 && maxSeenV < 512 && maxSeenV != 0) { + h = (int)maxSeenV; + } + } + + // TODO: In order to have the most stable hash possible, skip space between w/bufw? + // TODO: Use hash based on ini file, or always crc32c, or etc. + const u32 sizeInRAM = (textureBitsPerPixel[fmt] * bufw * h) / 8; + const u32 *checkp = (const u32 *)Memory::GetPointer(addr); + return DoQuickTexHash(checkp, sizeInRAM); } ReplacedTexture TextureReplacer::FindReplacement(u32 hash) { @@ -77,6 +91,11 @@ void TextureReplacer::NotifyTextureDecoded(u32 hash, const void *data, int pitch // TODO } +bool TextureReplacer::LookupHashRange(u32 addr, int &w, int &h) { + // TODO: Pull from table loaded via ini. + return false; +} + void ReplacedTexture::Load(int level, void *out, int rowPitch) { _assert_msg_(G3D, (size_t)level < levels_.size(), "Invalid miplevel"); _assert_msg_(G3D, out != nullptr && rowPitch > 0, "Invalid out/pitch"); diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h index 78ccc9717597..5d2ce7026c48 100644 --- a/GPU/Common/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -102,6 +102,8 @@ class TextureReplacer { void NotifyTextureDecoded(u32 hash, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt); protected: + bool LookupHashRange(u32 addr, int &w, int &h); + bool enabled_; std::string gameID_; std::string basePath_; From 5dbc2b9267e6f3b7f97dc128ba071ad816897176 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 15:03:39 -0700 Subject: [PATCH 04/34] Initial support for saving textures to PNGs. --- GPU/Common/TextureReplacer.cpp | 84 ++++++++++++++++++++++++++++++- GPU/Common/TextureReplacer.h | 5 +- GPU/Directx9/TextureCacheDX9.cpp | 2 +- GPU/GLES/TextureCache.cpp | 7 ++- GPU/Vulkan/TextureCacheVulkan.cpp | 2 +- 5 files changed, 91 insertions(+), 9 deletions(-) diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 67c1beff6e9d..adc6c737bcf9 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -15,7 +15,13 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#ifndef USING_QT_UI +// TODO: Make this and change the include path? Or move to Core? +#include "ext/libpng17/png.h" +#endif + #include "ext/xxhash.h" +#include "Common/ColorConv.h" #include "Common/FileUtil.h" #include "Core/Config.h" #include "Core/System.h" @@ -81,14 +87,88 @@ ReplacedTexture TextureReplacer::FindReplacement(u32 hash) { return result; } -void TextureReplacer::NotifyTextureDecoded(u32 hash, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt) { +#ifndef USING_QT_UI +static bool WriteTextureToPNG(png_imagep image, const std::string &filename, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) { + FILE *fp = File::OpenCFile(filename, "wb"); + if (!fp) { + ERROR_LOG(COMMON, "Unable to open texture file for writing."); + return false; + } + + if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, row_stride, colormap)) { + if (fclose(fp) != 0) { + ERROR_LOG(COMMON, "Texture file write failed."); + return false; + } + return true; + } else { + ERROR_LOG(COMMON, "Texture PNG encode failed."); + fclose(fp); + remove(filename.c_str()); + return false; + } +} +#endif + +void TextureReplacer::NotifyTextureDecoded(u32 hash, u32 addr, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt) { _assert_msg_(G3D, enabled_, "Replacement not enabled"); if (!g_Config.bSaveNewTextures) { // Ignore. return; } - // TODO + char hashname[8 + 4 + 1] = {}; + snprintf(hashname, sizeof(hashname), "%08x.png", hash); + const std::string filename = basePath_ + hashname; + + // TODO: Check for ini ignored or aliased textures. + + if (File::Exists(filename)) { + // Must've been decoded and saved as a new texture already. + return; + } + +#ifdef USING_QT_UI + ERROR_LOG(G3D, "Replacement texture saving not implemented for Qt"); +#else + if (fmt != ReplacedTextureFormat::F_8888) { + saveBuf.resize((pitch * h) / sizeof(u16)); + switch (fmt) { + case ReplacedTextureFormat::F_5650: + ConvertRGBA565ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16)); + break; + case ReplacedTextureFormat::F_5551: + ConvertRGBA5551ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16)); + break; + case ReplacedTextureFormat::F_4444: + ConvertRGBA4444ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16)); + break; + case ReplacedTextureFormat::F_8888_BGRA: + ConvertBGRA8888ToRGBA8888(saveBuf.data(), (const u32 *)data, (pitch * h) / sizeof(u32)); + break; + } + + data = saveBuf.data(); + } + + // Only save the hashed portion of the PNG. + LookupHashRange(addr, w, h); + + png_image png; + memset(&png, 0, sizeof(png)); + png.version = PNG_IMAGE_VERSION; + png.format = PNG_FORMAT_RGBA; + png.width = w; + png.height = h; + bool success = WriteTextureToPNG(&png, filename, 0, data, pitch, nullptr); + png_image_free(&png); + + if (png.warning_or_error >= 2) { + ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors."); + } else if (success) { + NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", hash, w, h); + } +#endif } bool TextureReplacer::LookupHashRange(u32 addr, int &w, int &h) { diff --git a/GPU/Common/TextureReplacer.h b/GPU/Common/TextureReplacer.h index 5d2ce7026c48..fd02d6db50bf 100644 --- a/GPU/Common/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -19,6 +19,7 @@ #include #include "Common/Common.h" +#include "Common/MemoryUtil.h" #include "GPU/ge_constants.h" class TextureCacheCommon; @@ -29,6 +30,7 @@ enum class ReplacedTextureFormat { F_5551, F_4444, F_8888, + F_8888_BGRA, }; // These must match the constants in TextureCacheCommon. @@ -99,11 +101,12 @@ class TextureReplacer { ReplacedTexture FindReplacement(u32 hash); - void NotifyTextureDecoded(u32 hash, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt); + void NotifyTextureDecoded(u32 hash, u32 addr, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt); protected: bool LookupHashRange(u32 addr, int &w, int &h); + SimpleBuf saveBuf; bool enabled_; std::string gameID_; std::string basePath_; diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 31bd902e83fb..957cc14fd95c 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -1674,7 +1674,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re if (replacer.Enabled()) { int bpp = dstFmt == D3DFMT_A8R8G8B8 ? 4 : 2; - replacer.NotifyTextureDecoded(entry.fullhash, pixelData, w * bpp, w, h, FromD3D9Format(dstFmt)); + replacer.NotifyTextureDecoded(entry.fullhash, entry.addr, pixelData, w * bpp, w, h, FromD3D9Format(dstFmt)); } } diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index d5a9599912c2..a864d45112f7 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -993,7 +993,7 @@ bool TextureCache::SetOffsetTexture(u32 offset) { return false; } -ReplacedTextureFormat FromGLESFormat(GLenum fmt) { +ReplacedTextureFormat FromGLESFormat(GLenum fmt, bool useBGRA = false) { // TODO: 16-bit formats are incorrect, since swizzled. switch (fmt) { case GL_UNSIGNED_SHORT_5_6_5: @@ -1004,7 +1004,7 @@ ReplacedTextureFormat FromGLESFormat(GLenum fmt) { return ReplacedTextureFormat::F_4444; case GL_UNSIGNED_BYTE: default: - return ReplacedTextureFormat::F_8888; + return useBGRA ? ReplacedTextureFormat::F_8888_BGRA : ReplacedTextureFormat::F_8888; } } @@ -1813,8 +1813,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &repla if (replacer.Enabled()) { int bpp = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; - // TODO: BGRA. - replacer.NotifyTextureDecoded(entry.fullhash, pixelData, w * bpp, w, h, FromGLESFormat(dstFmt)); + replacer.NotifyTextureDecoded(entry.fullhash, entry.addr, pixelData, (useUnpack ? bufw : w) * bpp, w, h, FromGLESFormat(dstFmt, useBGRA)); } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 82d9a93f1f88..f31c66e8f142 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1348,7 +1348,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { } else { LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); if (replacer.Enabled()) { - replacer.NotifyTextureDecoded(entry->fullhash, data, stride, mipWidth, mipHeight, FromVulkanFormat(actualFmt)); + replacer.NotifyTextureDecoded(entry->fullhash, texaddr, data, stride, mipWidth, mipHeight, FromVulkanFormat(actualFmt)); } } entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); From 9039dd606fe0d3674653c345fe01905e6159756b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 15:07:56 -0700 Subject: [PATCH 05/34] Move TextureReplacer to Core. Probably makes more sense here after all. --- CMakeLists.txt | 4 ++-- Core/Core.vcxproj | 2 ++ Core/Core.vcxproj.filters | 6 ++++++ {GPU/Common => Core}/TextureReplacer.cpp | 5 ++--- {GPU/Common => Core}/TextureReplacer.h | 0 GPU/Common/TextureCacheCommon.h | 2 +- GPU/GPU.vcxproj | 2 -- GPU/GPU.vcxproj.filters | 6 ------ Qt/GPU.pro | 1 - android/jni/Android.mk | 2 +- 10 files changed, 14 insertions(+), 16 deletions(-) rename {GPU/Common => Core}/TextureReplacer.cpp (97%) rename {GPU/Common => Core}/TextureReplacer.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5986403a05dc..254acf3daa3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1432,6 +1432,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Screenshot.h Core/System.cpp Core/System.h + Core/TextureReplacer.cpp + Core/TextureReplacer.h Core/Util/AudioFormat.cpp Core/Util/AudioFormat.h Core/Util/GameManager.cpp @@ -1491,8 +1493,6 @@ add_library(GPU OBJECT GPU/Common/TextureDecoder.h GPU/Common/TextureCacheCommon.cpp GPU/Common/TextureCacheCommon.h - GPU/Common/TextureReplacer.cpp - GPU/Common/TextureReplacer.h GPU/Common/TextureScalerCommon.cpp GPU/Common/TextureScalerCommon.h ${GPU_NEON} diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 514cf98e748d..6f255870a575 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -181,6 +181,7 @@ + @@ -506,6 +507,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 76cce1b92107..4d22852e1f9f 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -631,6 +631,9 @@ FileLoaders + + Core + @@ -1176,6 +1179,9 @@ FileLoaders + + Core + diff --git a/GPU/Common/TextureReplacer.cpp b/Core/TextureReplacer.cpp similarity index 97% rename from GPU/Common/TextureReplacer.cpp rename to Core/TextureReplacer.cpp index adc6c737bcf9..dbecb4d128f0 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -16,8 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #ifndef USING_QT_UI -// TODO: Make this and change the include path? Or move to Core? -#include "ext/libpng17/png.h" +#include #endif #include "ext/xxhash.h" @@ -25,9 +24,9 @@ #include "Common/FileUtil.h" #include "Core/Config.h" #include "Core/System.h" +#include "Core/TextureReplacer.h" #include "Core/ELF/ParamSFO.h" #include "GPU/Common/TextureDecoder.h" -#include "GPU/Common/TextureReplacer.h" TextureReplacer::TextureReplacer() : enabled_(false) { } diff --git a/GPU/Common/TextureReplacer.h b/Core/TextureReplacer.h similarity index 100% rename from GPU/Common/TextureReplacer.h rename to Core/TextureReplacer.h diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 17da5fbf76db..6dcec60e5b56 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -22,8 +22,8 @@ #include "Common/CommonTypes.h" #include "Common/MemoryUtil.h" +#include "Core/TextureReplacer.h" #include "GPU/Common/GPUDebugInterface.h" -#include "GPU/Common/TextureReplacer.h" enum TextureFiltering { TEX_FILTER_AUTO = 1, diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 1c6733df4255..5b49067da8b8 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -199,7 +199,6 @@ true - @@ -276,7 +275,6 @@ true - diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 2ebdc392476e..9818d21e9299 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -240,9 +240,6 @@ Vulkan - - Common - @@ -464,8 +461,5 @@ Vulkan - - Common - diff --git a/Qt/GPU.pro b/Qt/GPU.pro index ba4f607bf3b7..e993b64b5a54 100644 --- a/Qt/GPU.pro +++ b/Qt/GPU.pro @@ -54,7 +54,6 @@ SOURCES += $$P/GPU/GeDisasm.cpp \ # GPU $$P/GPU/Common/TextureScalerCommon.cpp \ $$P/GPU/Common/VertexDecoderCommon.cpp \ $$P/GPU/Common/TextureCacheCommon.cpp \ - $$P/GPU/Common/TextureReplacer.cpp \ $$P/GPU/Common/TransformCommon.cpp \ $$P/GPU/Common/SoftwareTransformCommon.cpp \ $$P/GPU/Common/PostShader.cpp \ diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 8f00efe94c29..82f5adb187cd 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -207,7 +207,6 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/SoftwareTransformCommon.cpp.arm \ $(SRC)/GPU/Common/VertexDecoderCommon.cpp.arm \ $(SRC)/GPU/Common/TextureCacheCommon.cpp.arm \ - $(SRC)/GPU/Common/TextureReplacer.cpp \ $(SRC)/GPU/Common/TextureScalerCommon.cpp.arm \ $(SRC)/GPU/Common/SplineCommon.cpp.arm \ $(SRC)/GPU/Common/DrawEngineCommon.cpp.arm \ @@ -269,6 +268,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/SaveState.cpp \ $(SRC)/Core/Screenshot.cpp \ $(SRC)/Core/System.cpp \ + $(SRC)/Core/TextureReplacer.cpp \ $(SRC)/Core/Debugger/Breakpoints.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ $(SRC)/Core/Dialog/PSPDialog.cpp \ From 565653c77b031dee1162ecb7a394e6e4c85cceab Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 15:18:45 -0700 Subject: [PATCH 06/34] Save and load textures using full key. Of course, need this, not just the hash. --- Core/TextureReplacer.cpp | 8 ++++---- Core/TextureReplacer.h | 4 ++-- GPU/Directx9/TextureCacheDX9.cpp | 4 ++-- GPU/GLES/TextureCache.cpp | 4 ++-- GPU/Vulkan/TextureCacheVulkan.cpp | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index dbecb4d128f0..cf72d94687b8 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -73,7 +73,7 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm return DoQuickTexHash(checkp, sizeInRAM); } -ReplacedTexture TextureReplacer::FindReplacement(u32 hash) { +ReplacedTexture TextureReplacer::FindReplacement(u64 cachekey, u32 hash) { _assert_msg_(G3D, enabled_, "Replacement not enabled"); ReplacedTexture result; @@ -109,15 +109,15 @@ static bool WriteTextureToPNG(png_imagep image, const std::string &filename, int } #endif -void TextureReplacer::NotifyTextureDecoded(u32 hash, u32 addr, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt) { +void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt) { _assert_msg_(G3D, enabled_, "Replacement not enabled"); if (!g_Config.bSaveNewTextures) { // Ignore. return; } - char hashname[8 + 4 + 1] = {}; - snprintf(hashname, sizeof(hashname), "%08x.png", hash); + char hashname[16 + 8 + 4 + 1] = {}; + snprintf(hashname, sizeof(hashname), "%016llx%08x.png", cachekey, hash); const std::string filename = basePath_ + hashname; // TODO: Check for ini ignored or aliased textures. diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index fd02d6db50bf..7d758dfa2bf2 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -99,9 +99,9 @@ class TextureReplacer { u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV); - ReplacedTexture FindReplacement(u32 hash); + ReplacedTexture FindReplacement(u64 cachekey, u32 hash); - void NotifyTextureDecoded(u32 hash, u32 addr, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt); + void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt); protected: bool LookupHashRange(u32 addr, int &w, int &h); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 957cc14fd95c..309397873ea4 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -1255,7 +1255,7 @@ void TextureCacheDX9::SetTexture(bool force) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(entry->fullhash); + ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; @@ -1674,7 +1674,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re if (replacer.Enabled()) { int bpp = dstFmt == D3DFMT_A8R8G8B8 ? 4 : 2; - replacer.NotifyTextureDecoded(entry.fullhash, entry.addr, pixelData, w * bpp, w, h, FromD3D9Format(dstFmt)); + replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash, entry.addr, pixelData, w * bpp, w, h, FromD3D9Format(dstFmt)); } } diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index a864d45112f7..cd3c6c10832b 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -1364,7 +1364,7 @@ void TextureCache::SetTexture(bool force) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(entry->fullhash); + ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; @@ -1813,7 +1813,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &repla if (replacer.Enabled()) { int bpp = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; - replacer.NotifyTextureDecoded(entry.fullhash, entry.addr, pixelData, (useUnpack ? bufw : w) * bpp, w, h, FromGLESFormat(dstFmt, useBGRA)); + replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash,entry.addr, pixelData, (useUnpack ? bufw : w) * bpp, w, h, FromGLESFormat(dstFmt, useBGRA)); } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index f31c66e8f142..e7741eca46ea 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1237,7 +1237,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(entry->fullhash); + ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; @@ -1348,7 +1348,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { } else { LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); if (replacer.Enabled()) { - replacer.NotifyTextureDecoded(entry->fullhash, texaddr, data, stride, mipWidth, mipHeight, FromVulkanFormat(actualFmt)); + replacer.NotifyTextureDecoded(cachekey, entry->fullhash, texaddr, data, stride, mipWidth, mipHeight, FromVulkanFormat(actualFmt)); } } entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); From d6e5df6f21080919bfe760006c5917caadd8573e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 15:21:48 -0700 Subject: [PATCH 07/34] Save individual mip levels. --- Core/TextureReplacer.cpp | 10 +++++++--- Core/TextureReplacer.h | 2 +- GPU/Directx9/TextureCacheDX9.cpp | 2 +- GPU/GLES/TextureCache.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index cf72d94687b8..54144dfe0bd8 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -109,15 +109,19 @@ static bool WriteTextureToPNG(png_imagep image, const std::string &filename, int } #endif -void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt) { +void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, ReplacedTextureFormat fmt) { _assert_msg_(G3D, enabled_, "Replacement not enabled"); if (!g_Config.bSaveNewTextures) { // Ignore. return; } - char hashname[16 + 8 + 4 + 1] = {}; - snprintf(hashname, sizeof(hashname), "%016llx%08x.png", cachekey, hash); + char hashname[16 + 8 + 4 + 1 + 11 + 1] = {}; + if (level > 0) { + snprintf(hashname, sizeof(hashname), "%016llx%08x_%d.png", cachekey, hash, level); + } else { + snprintf(hashname, sizeof(hashname), "%016llx%08x.png", cachekey, hash); + } const std::string filename = basePath_ + hashname; // TODO: Check for ini ignored or aliased textures. diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 7d758dfa2bf2..e155bd5b1178 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -101,7 +101,7 @@ class TextureReplacer { ReplacedTexture FindReplacement(u64 cachekey, u32 hash); - void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int w, int h, ReplacedTextureFormat fmt); + void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, ReplacedTextureFormat fmt); protected: bool LookupHashRange(u32 addr, int &w, int &h); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 309397873ea4..c28146464758 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -1674,7 +1674,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re if (replacer.Enabled()) { int bpp = dstFmt == D3DFMT_A8R8G8B8 ? 4 : 2; - replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash, entry.addr, pixelData, w * bpp, w, h, FromD3D9Format(dstFmt)); + replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash, entry.addr, pixelData, w * bpp, level, w, h, FromD3D9Format(dstFmt)); } } diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index cd3c6c10832b..a7a8f7f5796e 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -1813,7 +1813,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &repla if (replacer.Enabled()) { int bpp = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; - replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash,entry.addr, pixelData, (useUnpack ? bufw : w) * bpp, w, h, FromGLESFormat(dstFmt, useBGRA)); + replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash,entry.addr, pixelData, (useUnpack ? bufw : w) * bpp, level, w, h, FromGLESFormat(dstFmt, useBGRA)); } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index e7741eca46ea..ce9d22298ae0 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1348,7 +1348,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { } else { LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); if (replacer.Enabled()) { - replacer.NotifyTextureDecoded(cachekey, entry->fullhash, texaddr, data, stride, mipWidth, mipHeight, FromVulkanFormat(actualFmt)); + replacer.NotifyTextureDecoded(cachekey, entry->fullhash, texaddr, data, stride, i, mipWidth, mipHeight, FromVulkanFormat(actualFmt)); } } entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); From 59ada74deb0c4df1476d2e1e83b40889e8cb63a7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 15:41:12 -0700 Subject: [PATCH 08/34] Allow hashes to be ignored explicitly. Still need the actual ini hookup. --- Core/TextureReplacer.cpp | 39 +++++++++++++++++++++++++++------------ Core/TextureReplacer.h | 3 +++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 54144dfe0bd8..274a9ac5f84b 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -81,7 +81,12 @@ ReplacedTexture TextureReplacer::FindReplacement(u64 cachekey, u32 hash) { // Only actually replace if we're replacing. We might just be saving. if (g_Config.bReplaceTextures) { - // TODO + std::string hashfile = LookupHashFile(cachekey, hash, 0); + const std::string filename = basePath_ + hashfile; + + if (!hashfile.empty() && File::Exists(filename)) { + // TODO: Count levels that exist, etc. + } } return result; } @@ -116,18 +121,12 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con return; } - char hashname[16 + 8 + 4 + 1 + 11 + 1] = {}; - if (level > 0) { - snprintf(hashname, sizeof(hashname), "%016llx%08x_%d.png", cachekey, hash, level); - } else { - snprintf(hashname, sizeof(hashname), "%016llx%08x.png", cachekey, hash); - } - const std::string filename = basePath_ + hashname; + std::string hashfile = LookupHashFile(cachekey, hash, level); + const std::string filename = basePath_ + hashfile; - // TODO: Check for ini ignored or aliased textures. - - if (File::Exists(filename)) { - // Must've been decoded and saved as a new texture already. + // If it's empty, it's an ignored hash, we intentionally don't save. + if (hashfile.empty() || File::Exists(filename)) { + // If it exists, must've been decoded and saved as a new texture already. return; } @@ -174,6 +173,22 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con #endif } +std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { + // TODO: Look up via ini for alias / ignored. + return HashName(cachekey, hash, level) + ".png"; +} + +std::string TextureReplacer::HashName(u64 cachekey, u32 hash, int level) { + char hashname[16 + 8 + 1 + 11 + 1] = {}; + if (level > 0) { + snprintf(hashname, sizeof(hashname), "%016llx%08x_%d.png", cachekey, hash, level); + } else { + snprintf(hashname, sizeof(hashname), "%016llx%08x.png", cachekey, hash); + } + + return hashname; +} + bool TextureReplacer::LookupHashRange(u32 addr, int &w, int &h) { // TODO: Pull from table loaded via ini. return false; diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index e155bd5b1178..0e6a5bff5810 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include "Common/Common.h" #include "Common/MemoryUtil.h" @@ -105,6 +106,8 @@ class TextureReplacer { protected: bool LookupHashRange(u32 addr, int &w, int &h); + std::string LookupHashFile(u64 cachekey, u32 hash, int level); + std::string HashName(u64 cachekey, u32 hash, int level); SimpleBuf saveBuf; bool enabled_; From 4f3bac1b0a6d8de657ecffed19ef62313b6d55b1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 16:21:16 -0700 Subject: [PATCH 09/34] Actually load the texture replacement ini file. --- Core/TextureReplacer.cpp | 117 +++++++++++++++++++++++++++++++++++++-- Core/TextureReplacer.h | 13 +++++ 2 files changed, 125 insertions(+), 5 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 274a9ac5f84b..c1a34cca538b 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -20,6 +20,7 @@ #endif #include "ext/xxhash.h" +#include "file/ini_file.h" #include "Common/ColorConv.h" #include "Common/FileUtil.h" #include "Core/Config.h" @@ -28,6 +29,9 @@ #include "Core/ELF/ParamSFO.h" #include "GPU/Common/TextureDecoder.h" +static const std::string INI_FILENAME = "textures.ini"; +static const int VERSION = 1; + TextureReplacer::TextureReplacer() : enabled_(false) { } @@ -53,7 +57,91 @@ void TextureReplacer::NotifyConfigChanged() { enabled_ = File::Exists(basePath_) && File::IsDirectory(basePath_); } - // TODO: Load ini file. + if (enabled_) { + enabled_ = LoadIni(); + } +} + +bool TextureReplacer::LoadIni() { + // TODO: Use crc32c? + hash_ = ReplacedTextureHash::QUICK; + aliases_.clear(); + hashranges_.clear(); + + if (File::Exists(basePath_ + INI_FILENAME)) { + IniFile ini; + ini.LoadFromVFS(basePath_ + INI_FILENAME); + + auto options = ini.GetOrCreateSection("options"); + std::string hash; + options->Get("hash", &hash, ""); + // TODO: crc32c. + if (hash == "quick") { + hash_ = ReplacedTextureHash::QUICK; + } else { + ERROR_LOG(G3D, "Unsupported hash type: %s", hash.c_str()); + return false; + } + + int version = 0; + if (options->Get("version", &version, 0) && version > VERSION) { + ERROR_LOG(G3D, "Unsupported texture replacement version %d, trying anyway", version); + } + + std::vector hashNames; + if (ini.GetKeys("hashes", hashNames)) { + auto hashes = ini.GetOrCreateSection("hashes"); + // Format: hashname = filename.png + for (std::string hashName : hashNames) { + hashes->Get(hashName.c_str(), &aliases_[hashName], ""); + } + } + + std::vector hashrangeKeys; + if (ini.GetKeys("hashranges", hashrangeKeys)) { + auto hashranges = ini.GetOrCreateSection("hashranges"); + // Format: addr,w,h = newW,newH + for (std::string key : hashrangeKeys) { + std::string value; + if (hashranges->Get(key.c_str(), &value, "")) { + ParseHashRange(key, value); + } + } + } + } + + // The ini doesn't have to exist for it to be valid. + return true; +} + +void TextureReplacer::ParseHashRange(const std::string &key, const std::string &value) { + std::vector keyParts; + SplitString(key, ',', keyParts); + std::vector valueParts; + SplitString(value, ',', valueParts); + + if (keyParts.size() != 3 || valueParts.size() != 2) { + ERROR_LOG(G3D, "Ignoring invalid hashrange %s = %s, expecting addr,w,h = w,h", key.c_str(), value.c_str()); + return; + } + + u32 addr; + u32 fromW; + u32 fromH; + if (!TryParse(keyParts[0], &addr) || !TryParse(keyParts[1], &fromW) || !TryParse(keyParts[2], &fromH)) { + ERROR_LOG(G3D, "Ignoring invalid hashrange %s = %s, key format is 0x12345678,512,512", key.c_str(), value.c_str()); + return; + } + + u32 toW; + u32 toH; + if (!TryParse(valueParts[0], &toW) || !TryParse(valueParts[1], &toH)) { + ERROR_LOG(G3D, "Ignoring invalid hashrange %s = %s, value format is 512,512", key.c_str(), value.c_str()); + return; + } + + const u64 rangeKey = ((u64)addr << 32) | (fromW << 16) | fromH; + hashranges_[rangeKey] = WidthHeightPair(toW, toH); } u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV) { @@ -70,7 +158,12 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm // TODO: Use hash based on ini file, or always crc32c, or etc. const u32 sizeInRAM = (textureBitsPerPixel[fmt] * bufw * h) / 8; const u32 *checkp = (const u32 *)Memory::GetPointer(addr); - return DoQuickTexHash(checkp, sizeInRAM); + switch (hash_) { + case ReplacedTextureHash::QUICK: + return DoQuickTexHash(checkp, sizeInRAM); + default: + return 0; + } } ReplacedTexture TextureReplacer::FindReplacement(u64 cachekey, u32 hash) { @@ -174,8 +267,14 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con } std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { - // TODO: Look up via ini for alias / ignored. - return HashName(cachekey, hash, level) + ".png"; + const std::string hashname = HashName(cachekey, hash, level); + auto alias = aliases_.find(hashname); + if (alias != aliases_.end()) { + // Note: this will be blank if explicitly ignored. + return alias->second; + } + + return hashname + ".png"; } std::string TextureReplacer::HashName(u64 cachekey, u32 hash, int level) { @@ -190,7 +289,15 @@ std::string TextureReplacer::HashName(u64 cachekey, u32 hash, int level) { } bool TextureReplacer::LookupHashRange(u32 addr, int &w, int &h) { - // TODO: Pull from table loaded via ini. + const u64 rangeKey = ((u64)addr << 32) | (w << 16) | h; + auto range = hashranges_.find(rangeKey); + if (range != hashranges_.end()) { + const WidthHeightPair &wh = range->second; + w = wh.first; + h = wh.second; + return true; + } + return false; } diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 0e6a5bff5810..0628e1f01ded 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include "Common/Common.h" #include "Common/MemoryUtil.h" @@ -41,6 +42,12 @@ enum class ReplacedTextureAlpha { SIMPLE = 0x08, }; +// For forward comatibility, we specify the hash. +enum class ReplacedTextureHash { + // TODO: Maybe only support crc32c for now? + QUICK, +}; + struct ReplacedTexureLevel { int w; int h; @@ -105,6 +112,8 @@ class TextureReplacer { void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, ReplacedTextureFormat fmt); protected: + bool LoadIni(); + void ParseHashRange(const std::string &key, const std::string &value); bool LookupHashRange(u32 addr, int &w, int &h); std::string LookupHashFile(u64 cachekey, u32 hash, int level); std::string HashName(u64 cachekey, u32 hash, int level); @@ -113,4 +122,8 @@ class TextureReplacer { bool enabled_; std::string gameID_; std::string basePath_; + ReplacedTextureHash hash_; + std::unordered_map aliases_; + typedef std::pair WidthHeightPair; + std::unordered_map hashranges_; }; From f039259a1a6ea7e52fae085d8ea8353c31bee58c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 16:26:18 -0700 Subject: [PATCH 10/34] Use a same-everywhere quick hash for now. --- Core/TextureReplacer.cpp | 2 +- GPU/Common/TextureDecoder.cpp | 2 ++ GPU/Common/TextureDecoder.h | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index c1a34cca538b..c58142baa515 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -160,7 +160,7 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm const u32 *checkp = (const u32 *)Memory::GetPointer(addr); switch (hash_) { case ReplacedTextureHash::QUICK: - return DoQuickTexHash(checkp, sizeInRAM); + return StableQuickTexHash(checkp, sizeInRAM); default: return 0; } diff --git a/GPU/Common/TextureDecoder.cpp b/GPU/Common/TextureDecoder.cpp index 961dca9f3623..bb0afee365be 100644 --- a/GPU/Common/TextureDecoder.cpp +++ b/GPU/Common/TextureDecoder.cpp @@ -301,6 +301,7 @@ void DoUnswizzleTex16Basic(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32 #ifndef _M_SSE #ifndef ARM64 QuickTexHashFunc DoQuickTexHash = &QuickTexHashBasic; +QuickTexHashFunc StableQuickTexHash = &QuickTexHashNonSSE; UnswizzleTex16Func DoUnswizzleTex16 = &DoUnswizzleTex16Basic; ReliableHash32Func DoReliableHash32 = &XXH32; ReliableHash64Func DoReliableHash64 = &XXH64; @@ -312,6 +313,7 @@ void SetupTextureDecoder() { #ifdef HAVE_ARMV7 if (cpu_info.bNEON) { DoQuickTexHash = &QuickTexHashNEON; + StableQuickTexHash = &QuickTexHashNEON; DoUnswizzleTex16 = &DoUnswizzleTex16NEON; #ifndef IOS // Not sure if this is safe on iOS, it's had issues with xxhash. diff --git a/GPU/Common/TextureDecoder.h b/GPU/Common/TextureDecoder.h index d3ddca94758c..6b20498e1ba6 100644 --- a/GPU/Common/TextureDecoder.h +++ b/GPU/Common/TextureDecoder.h @@ -39,6 +39,7 @@ void DoSwizzleTex16(const u32 *ysrcp, u8 *texptr, int bxc, int byc, u32 pitch); #if defined(_M_SSE) u32 QuickTexHashSSE2(const void *checkp, u32 size); #define DoQuickTexHash QuickTexHashSSE2 +#define StableQuickTexHash QuickTexHashSSE2 // Pitch must be aligned to 16 bits (as is the case on a PSP) void DoUnswizzleTex16Basic(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32 pitch); @@ -59,6 +60,7 @@ typedef u32 ReliableHashType; // For ARM64, NEON is mandatory, so we also statically link. #elif defined(ARM64) #define DoQuickTexHash QuickTexHashNEON +#define StableQuickTexHash QuickTexHashNEON #define DoUnswizzleTex16 DoUnswizzleTex16NEON #define DoReliableHash32 ReliableHash32NEON @@ -71,6 +73,7 @@ typedef u64 ReliableHashType; #else typedef u32 (*QuickTexHashFunc)(const void *checkp, u32 size); extern QuickTexHashFunc DoQuickTexHash; +extern QuickTexHashFunc StableQuickTexHash; typedef void (*UnswizzleTex16Func)(const u8 *texptr, u32 *ydestp, int bxc, int byc, u32 pitch); extern UnswizzleTex16Func DoUnswizzleTex16; From 0c357c0ea372dacd5b6d33e31d5d2e6414e3550c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 16:29:46 -0700 Subject: [PATCH 11/34] Pass w/h in for replacement lookup. So we can scale the w/h properly. --- Core/TextureReplacer.cpp | 3 ++- Core/TextureReplacer.h | 2 +- GPU/Directx9/TextureCacheDX9.cpp | 2 +- GPU/GLES/TextureCache.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index c58142baa515..7fa1fd62ca65 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -166,7 +166,7 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm } } -ReplacedTexture TextureReplacer::FindReplacement(u64 cachekey, u32 hash) { +ReplacedTexture TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w, int h) { _assert_msg_(G3D, enabled_, "Replacement not enabled"); ReplacedTexture result; @@ -179,6 +179,7 @@ ReplacedTexture TextureReplacer::FindReplacement(u64 cachekey, u32 hash) { if (!hashfile.empty() && File::Exists(filename)) { // TODO: Count levels that exist, etc. + // TODO: Use w/h to determine actual size based on hash range (png may be smaller)? } } return result; diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 0628e1f01ded..dcb91cda084f 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -107,7 +107,7 @@ class TextureReplacer { u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV); - ReplacedTexture FindReplacement(u64 cachekey, u32 hash); + ReplacedTexture FindReplacement(u64 cachekey, u32 hash, int w, int h); void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, ReplacedTextureFormat fmt); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index c28146464758..8133b508a6b9 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -1255,7 +1255,7 @@ void TextureCacheDX9::SetTexture(bool force) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash); + ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index a7a8f7f5796e..3f695c668c98 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -1364,7 +1364,7 @@ void TextureCache::SetTexture(bool force) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash); + ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index ce9d22298ae0..a1418201f279 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1237,7 +1237,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash); + ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; From 120cd0fb7e97848a0a5118d863484346808b0928 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 16:33:09 -0700 Subject: [PATCH 12/34] Don't convert pixels we're not going to use anyway. --- Core/TextureReplacer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 7fa1fd62ca65..3c8c69622d5e 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -224,6 +224,9 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con return; } + // Only save the hashed portion of the PNG. + LookupHashRange(addr, w, h); + #ifdef USING_QT_UI ERROR_LOG(G3D, "Replacement texture saving not implemented for Qt"); #else @@ -247,9 +250,6 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con data = saveBuf.data(); } - // Only save the hashed portion of the PNG. - LookupHashRange(addr, w, h); - png_image png; memset(&png, 0, sizeof(png)); png.version = PNG_IMAGE_VERSION; From 6d0c7a9faf9cc399a7ccf4d5ca78510f51d50b26 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 17:30:32 -0700 Subject: [PATCH 13/34] Skip gaps in texture replacement hashing. This should hopefully provide the least duplicates possible. --- Core/TextureReplacer.cpp | 41 +++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 3c8c69622d5e..b006446c452b 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -154,15 +154,38 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm } } - // TODO: In order to have the most stable hash possible, skip space between w/bufw? - // TODO: Use hash based on ini file, or always crc32c, or etc. - const u32 sizeInRAM = (textureBitsPerPixel[fmt] * bufw * h) / 8; - const u32 *checkp = (const u32 *)Memory::GetPointer(addr); - switch (hash_) { - case ReplacedTextureHash::QUICK: - return StableQuickTexHash(checkp, sizeInRAM); - default: - return 0; + const u8 *checkp = Memory::GetPointer(addr); + if (bufw <= w) { + // We can assume the data is contiguous. These are the total used pixels. + const u32 totalPixels = bufw * h + (w - bufw); + const u32 sizeInRAM = (textureBitsPerPixel[fmt] * totalPixels) / 8; + + switch (hash_) { + case ReplacedTextureHash::QUICK: + return StableQuickTexHash(checkp, sizeInRAM); + default: + return 0; + } + } else { + // We have gaps. Let's hash each row and sum. + const u32 bytesPerLine = (textureBitsPerPixel[fmt] * w) / 8; + const u32 stride = (textureBitsPerPixel[fmt] * bufw) / 8; + + u32 result = 0; + switch (hash_) { + case ReplacedTextureHash::QUICK: + for (int y = 0; y < h; ++y) { + u32 rowHash = StableQuickTexHash(checkp, bytesPerLine); + result = (result * 11) ^ rowHash; + checkp += stride; + } + break; + + default: + break; + } + + return result; } } From c1a8edfedfcc10dfcea01ce0270b5a5659efe22d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 18:39:30 -0700 Subject: [PATCH 14/34] Replace textures from PNGs. --- Core/TextureReplacer.cpp | 69 +++++++++++++++++++++++++++++++++++----- Core/TextureReplacer.h | 1 + 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index b006446c452b..21993a7ca402 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -31,6 +31,7 @@ static const std::string INI_FILENAME = "textures.ini"; static const int VERSION = 1; +static const int MAX_MIP_LEVELS = 64; TextureReplacer::TextureReplacer() : enabled_(false) { } @@ -140,6 +141,11 @@ void TextureReplacer::ParseHashRange(const std::string &key, const std::string & return; } + if (toW > fromW || toH > fromH) { + ERROR_LOG(G3D, "Ignoring invalid hashrange %s = %s, range bigger than source", key.c_str(), value.c_str()); + return; + } + const u64 rangeKey = ((u64)addr << 32) | (fromW << 16) | fromH; hashranges_[rangeKey] = WidthHeightPair(toW, toH); } @@ -197,15 +203,45 @@ ReplacedTexture TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w, // Only actually replace if we're replacing. We might just be saving. if (g_Config.bReplaceTextures) { - std::string hashfile = LookupHashFile(cachekey, hash, 0); + PopulateReplacement(&result, cachekey, hash, w, h); + } + return result; +} + +void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h) { + int newW = w; + int newH = h; + LookupHashRange(cachekey >> 32, newW, newH); + + for (int i = 0; i < MAX_MIP_LEVELS; ++i) { + const std::string hashfile = LookupHashFile(cachekey, hash, i); const std::string filename = basePath_ + hashfile; + if (hashfile.empty() || !File::Exists(filename)) { + // Out of valid mip levels. Bail out. + break; + } - if (!hashfile.empty() && File::Exists(filename)) { - // TODO: Count levels that exist, etc. - // TODO: Use w/h to determine actual size based on hash range (png may be smaller)? + ReplacedTexureLevel level; + level.fmt = ReplacedTextureFormat::F_8888; + level.file = filename; + + png_image png = {}; + png.version = PNG_IMAGE_VERSION; + FILE *fp = File::OpenCFile(filename, "rb"); + if (png_image_begin_read_from_stdio(&png, fp)) { + // We pad files that have been hashrange'd so they are the same texture size. + level.w = (png.width * w) / newW; + level.h = (png.height * h) / newH; + + result->levels_.push_back(level); + } else { + ERROR_LOG(G3D, "Could not load texture replacement info: %s", filename.c_str()); } + fclose(fp); + png_image_free(&png); } - return result; + + // TODO: Could calculate alpha status, or maybe from ini? Let's ignore for now. } #ifndef USING_QT_UI @@ -304,9 +340,9 @@ std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { std::string TextureReplacer::HashName(u64 cachekey, u32 hash, int level) { char hashname[16 + 8 + 1 + 11 + 1] = {}; if (level > 0) { - snprintf(hashname, sizeof(hashname), "%016llx%08x_%d.png", cachekey, hash, level); + snprintf(hashname, sizeof(hashname), "%016llx%08x_%d", cachekey, hash, level); } else { - snprintf(hashname, sizeof(hashname), "%016llx%08x.png", cachekey, hash); + snprintf(hashname, sizeof(hashname), "%016llx%08x", cachekey, hash); } return hashname; @@ -329,5 +365,22 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) { _assert_msg_(G3D, (size_t)level < levels_.size(), "Invalid miplevel"); _assert_msg_(G3D, out != nullptr && rowPitch > 0, "Invalid out/pitch"); - // TODO + const ReplacedTexureLevel &info = levels_[level]; + + png_image png = {}; + png.version = PNG_IMAGE_VERSION; + png.format = PNG_FORMAT_RGBA; + + FILE *fp = File::OpenCFile(info.file, "rb"); + if (!png_image_begin_read_from_stdio(&png, fp)) { + ERROR_LOG(G3D, "Could not load texture replacement info: %s", info.file.c_str()); + return; + } + if (!png_image_finish_read(&png, nullptr, out, rowPitch, nullptr)) { + ERROR_LOG(G3D, "Could not load texture replacement: %s", info.file.c_str()); + return; + } + + fclose(fp); + png_image_free(&png); } diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index dcb91cda084f..806ee6059d2f 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -117,6 +117,7 @@ class TextureReplacer { bool LookupHashRange(u32 addr, int &w, int &h); std::string LookupHashFile(u64 cachekey, u32 hash, int level); std::string HashName(u64 cachekey, u32 hash, int level); + void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h); SimpleBuf saveBuf; bool enabled_; From 149de4147a9eb7d1f178929a8b3275880639fb19 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 18:39:40 -0700 Subject: [PATCH 15/34] Skip replacements for PPGe textures. --- Core/TextureReplacer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 21993a7ca402..abb78e279c7b 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -273,6 +273,10 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con // Ignore. return; } + if (addr > 0x05000000 && addr < 0x08800000) { + // Don't save the PPGe texture. + return; + } std::string hashfile = LookupHashFile(cachekey, hash, level); const std::string filename = basePath_ + hashfile; From 9ffc717de10758caeeb0d2273665cbe99acd7528 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 19:00:20 -0700 Subject: [PATCH 16/34] Properly save 16-bit textures for replacements. --- Common/ColorConv.cpp | 33 +++++++++++++++++++++++++++++++++ Common/ColorConv.h | 4 ++++ Core/TextureReplacer.cpp | 13 +++++++++++++ Core/TextureReplacer.h | 3 +++ GPU/GLES/TextureCache.cpp | 6 +++--- 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Common/ColorConv.cpp b/Common/ColorConv.cpp index a318fedd868b..9dcd1876f98c 100644 --- a/Common/ColorConv.cpp +++ b/Common/ColorConv.cpp @@ -435,6 +435,39 @@ void ConvertRGBA4444ToRGBA8888(u32 *dst32, const u16 *src, const u32 numPixels) } } +void ConvertABGR565ToRGBA8888(u32 *dst32, const u16 *src, const u32 numPixels) { + u8 *dst = (u8 *)dst32; + for (u32 x = 0; x < numPixels; x++) { + u16 col = src[x]; + dst[x * 4] = Convert5To8((col >> 11) & 0x1f); + dst[x * 4 + 1] = Convert6To8((col >> 5) & 0x3f); + dst[x * 4 + 2] = Convert5To8((col) & 0x1f); + dst[x * 4 + 3] = 255; + } +} + +void ConvertABGR1555ToRGBA8888(u32 *dst32, const u16 *src, const u32 numPixels) { + u8 *dst = (u8 *)dst32; + for (u32 x = 0; x < numPixels; x++) { + u16 col = src[x]; + dst[x * 4] = Convert5To8((col >> 11) & 0x1f); + dst[x * 4 + 1] = Convert5To8((col >> 6) & 0x1f); + dst[x * 4 + 2] = Convert5To8((col >> 1) & 0x1f); + dst[x * 4 + 3] = (col & 1) ? 255 : 0; + } +} + +void ConvertABGR4444ToRGBA8888(u32 *dst32, const u16 *src, const u32 numPixels) { + u8 *dst = (u8 *)dst32; + for (u32 x = 0; x < numPixels; x++) { + u16 col = src[x]; + dst[x * 4] = Convert4To8(col >> 12); + dst[x * 4 + 1] = Convert4To8((col >> 8) & 0xf); + dst[x * 4 + 2] = Convert4To8((col >> 4) & 0xf); + dst[x * 4 + 3] = Convert4To8(col & 0xf); + } +} + void ConvertRGBA4444ToBGRA8888(u32 *dst32, const u16 *src, const u32 numPixels) { u8 *dst = (u8 *)dst32; for (u32 x = 0; x < numPixels; x++) { diff --git a/Common/ColorConv.h b/Common/ColorConv.h index 5a3b71fb8495..5750922ba20e 100644 --- a/Common/ColorConv.h +++ b/Common/ColorConv.h @@ -126,6 +126,10 @@ void ConvertRGBA565ToRGBA8888(u32 *dst, const u16 *src, const u32 numPixels); void ConvertRGBA5551ToRGBA8888(u32 *dst, const u16 *src, const u32 numPixels); void ConvertRGBA4444ToRGBA8888(u32 *dst, const u16 *src, const u32 numPixels); +void ConvertABGR565ToRGBA8888(u32 *dst, const u16 *src, const u32 numPixels); +void ConvertABGR1555ToRGBA8888(u32 *dst, const u16 *src, const u32 numPixels); +void ConvertABGR4444ToRGBA8888(u32 *dst, const u16 *src, const u32 numPixels); + void ConvertRGBA4444ToBGRA8888(u32 *dst, const u16 *src, const u32 numPixels); void ConvertRGBA5551ToBGRA8888(u32 *dst, const u16 *src, const u32 numPixels); void ConvertRGB565ToBGRA8888(u32 *dst, const u16 *src, const u32 numPixels); diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index abb78e279c7b..8698b0d0f256 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -305,12 +305,25 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con case ReplacedTextureFormat::F_4444: ConvertRGBA4444ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16)); break; + case ReplacedTextureFormat::F_0565_ABGR: + ConvertABGR565ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16)); + break; + case ReplacedTextureFormat::F_1555_ABGR: + ConvertABGR1555ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16)); + break; + case ReplacedTextureFormat::F_4444_ABGR: + ConvertABGR4444ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16)); + break; case ReplacedTextureFormat::F_8888_BGRA: ConvertBGRA8888ToRGBA8888(saveBuf.data(), (const u32 *)data, (pitch * h) / sizeof(u32)); break; } data = saveBuf.data(); + if (fmt != ReplacedTextureFormat::F_8888_BGRA) { + // We doubled our pitch. + pitch *= 2; + } } png_image png; diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 806ee6059d2f..c3fd618267ca 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -32,6 +32,9 @@ enum class ReplacedTextureFormat { F_5551, F_4444, F_8888, + F_0565_ABGR, + F_1555_ABGR, + F_4444_ABGR, F_8888_BGRA, }; diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 3f695c668c98..c55e124e9f60 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -997,11 +997,11 @@ ReplacedTextureFormat FromGLESFormat(GLenum fmt, bool useBGRA = false) { // TODO: 16-bit formats are incorrect, since swizzled. switch (fmt) { case GL_UNSIGNED_SHORT_5_6_5: - return ReplacedTextureFormat::F_5650; + return ReplacedTextureFormat::F_0565_ABGR; case GL_UNSIGNED_SHORT_5_5_5_1: - return ReplacedTextureFormat::F_5551; + return ReplacedTextureFormat::F_1555_ABGR; case GL_UNSIGNED_SHORT_4_4_4_4: - return ReplacedTextureFormat::F_4444; + return ReplacedTextureFormat::F_4444_ABGR; case GL_UNSIGNED_BYTE: default: return useBGRA ? ReplacedTextureFormat::F_8888_BGRA : ReplacedTextureFormat::F_8888; From f536182b37f3bf20d2796c4c0dae9d9d4682f8c6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 19:35:35 -0700 Subject: [PATCH 17/34] Cache texture replacement lookup info. This way we can avoid hitting the disk where possible. We could even preload. --- Core/TextureReplacer.cpp | 21 +++++++++++++++------ Core/TextureReplacer.h | 26 +++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 8698b0d0f256..dbffd4f77318 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -34,6 +34,7 @@ static const int VERSION = 1; static const int MAX_MIP_LEVELS = 64; TextureReplacer::TextureReplacer() : enabled_(false) { + none_.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; } TextureReplacer::~TextureReplacer() { @@ -195,16 +196,24 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm } } -ReplacedTexture TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w, int h) { +ReplacedTexture &TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w, int h) { _assert_msg_(G3D, enabled_, "Replacement not enabled"); - ReplacedTexture result; - result.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; - // Only actually replace if we're replacing. We might just be saving. - if (g_Config.bReplaceTextures) { - PopulateReplacement(&result, cachekey, hash, w, h); + if (!g_Config.bReplaceTextures) { + return none_; + } + + ReplacementCacheKey replacementKey(cachekey, hash); + auto it = cache_.find(replacementKey); + if (it != cache_.end()) { + return it->second; } + + // Okay, let's construct the result. + ReplacedTexture &result = cache_[replacementKey]; + result.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; + PopulateReplacement(&result, cachekey, hash, w, h); return result; } diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index c3fd618267ca..f60bf97a0742 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -58,6 +58,27 @@ struct ReplacedTexureLevel { std::string file; }; +struct ReplacementCacheKey { + u64 cachekey; + u32 hash; + + ReplacementCacheKey(u64 c, u32 h) : cachekey(c), hash(h) { + } + + bool operator ==(const ReplacementCacheKey &k) const { + return k.cachekey == cachekey && k.hash == hash; + } +}; + +namespace std { + template <> + struct hash { + size_t operator()(const ReplacementCacheKey &k) const { + return std::hash()(k.cachekey ^ ((u64)k.hash << 32)); + } + }; +} + struct ReplacedTexture { inline bool Valid() { return !levels_.empty(); @@ -110,7 +131,7 @@ class TextureReplacer { u32 ComputeHash(u32 addr, int bufw, int w, int h, GETextureFormat fmt, u16 maxSeenV); - ReplacedTexture FindReplacement(u64 cachekey, u32 hash, int w, int h); + ReplacedTexture &FindReplacement(u64 cachekey, u32 hash, int w, int h); void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, ReplacedTextureFormat fmt); @@ -130,4 +151,7 @@ class TextureReplacer { std::unordered_map aliases_; typedef std::pair WidthHeightPair; std::unordered_map hashranges_; + + ReplacedTexture none_; + std::unordered_map cache_; }; From 7528605d3d22d555d848959a598f295cd139f75e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 20:17:03 -0700 Subject: [PATCH 18/34] Correct loading replaced upscaled textures. --- GPU/GLES/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index c55e124e9f60..d177a4f15448 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -1769,7 +1769,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &repla gpuStats.numTexturesDecoded++; - if (replaced.Valid()) { + if (replaced.GetSize(level, w, h)) { PROFILE_THIS_SCOPE("replacetex"); tmpTexBufRearrange.resize(w * h); From 7a4af06cee5272d15caee3291edd12b22801b5ec Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 20:21:08 -0700 Subject: [PATCH 19/34] Save new textures into a separate path. This way you can tell which ones you've done already. --- Core/TextureReplacer.cpp | 29 ++++++++++++++++++++++++----- Core/TextureReplacer.h | 5 +++-- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index dbffd4f77318..8661315928cb 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -30,6 +30,7 @@ #include "GPU/Common/TextureDecoder.h" static const std::string INI_FILENAME = "textures.ini"; +static const std::string NEW_TEXTURE_DIR = "new/"; static const int VERSION = 1; static const int MAX_MIP_LEVELS = 64; @@ -52,8 +53,8 @@ void TextureReplacer::NotifyConfigChanged() { basePath_ = GetSysDirectory(DIRECTORY_TEXTURES) + gameID_ + "/"; // If we're saving, auto-create the directory. - if (g_Config.bSaveNewTextures && !File::Exists(basePath_)) { - File::CreateFullPath(basePath_); + if (g_Config.bSaveNewTextures && !File::Exists(basePath_ + NEW_TEXTURE_DIR)) { + File::CreateFullPath(basePath_ + NEW_TEXTURE_DIR); } enabled_ = File::Exists(basePath_) && File::IsDirectory(basePath_); @@ -230,7 +231,7 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, break; } - ReplacedTexureLevel level; + ReplacedTextureLevel level; level.fmt = ReplacedTextureFormat::F_8888; level.file = filename; @@ -289,6 +290,7 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con std::string hashfile = LookupHashFile(cachekey, hash, level); const std::string filename = basePath_ + hashfile; + const std::string saveFilename = basePath_ + NEW_TEXTURE_DIR + hashfile; // If it's empty, it's an ignored hash, we intentionally don't save. if (hashfile.empty() || File::Exists(filename)) { @@ -296,6 +298,15 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con return; } + ReplacementCacheKey replacementKey(cachekey, hash); + auto it = savedCache_.find(replacementKey); + if (it != savedCache_.end() && File::Exists(saveFilename)) { + // We've already saved this texture. Let's only save if it's bigger (e.g. scaled now.) + if (it->second.w >= w && it->second.h >= h) { + return; + } + } + // Only save the hashed portion of the PNG. LookupHashRange(addr, w, h); @@ -341,7 +352,7 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con png.format = PNG_FORMAT_RGBA; png.width = w; png.height = h; - bool success = WriteTextureToPNG(&png, filename, 0, data, pitch, nullptr); + bool success = WriteTextureToPNG(&png, saveFilename, 0, data, pitch, nullptr); png_image_free(&png); if (png.warning_or_error >= 2) { @@ -350,6 +361,14 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", hash, w, h); } #endif + + // Remember that we've saved this for next time. + ReplacedTextureLevel saved; + saved.fmt = ReplacedTextureFormat::F_8888; + saved.file = filename; + saved.w = w; + saved.h = h; + savedCache_[replacementKey] = saved; } std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { @@ -391,7 +410,7 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) { _assert_msg_(G3D, (size_t)level < levels_.size(), "Invalid miplevel"); _assert_msg_(G3D, out != nullptr && rowPitch > 0, "Invalid out/pitch"); - const ReplacedTexureLevel &info = levels_[level]; + const ReplacedTextureLevel &info = levels_[level]; png_image png = {}; png.version = PNG_IMAGE_VERSION; diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index f60bf97a0742..f9cc69dd3934 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -51,7 +51,7 @@ enum class ReplacedTextureHash { QUICK, }; -struct ReplacedTexureLevel { +struct ReplacedTextureLevel { int w; int h; ReplacedTextureFormat fmt; @@ -111,7 +111,7 @@ struct ReplacedTexture { void Load(int level, void *out, int rowPitch); protected: - std::vector levels_; + std::vector levels_; ReplacedTextureAlpha alphaStatus_; friend TextureReplacer; @@ -154,4 +154,5 @@ class TextureReplacer { ReplacedTexture none_; std::unordered_map cache_; + std::unordered_map savedCache_; }; From e1fd6b6f21dca0d1d1ac8b73153cd0cee6490162 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 20:33:14 -0700 Subject: [PATCH 20/34] Account for scaleFactor when saving clipped PNG. --- Core/TextureReplacer.cpp | 13 +++++++++---- Core/TextureReplacer.h | 2 +- GPU/Directx9/TextureCacheDX9.cpp | 2 +- GPU/GLES/TextureCache.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 8661315928cb..51e08cb22d34 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -95,7 +95,7 @@ bool TextureReplacer::LoadIni() { if (ini.GetKeys("hashes", hashNames)) { auto hashes = ini.GetOrCreateSection("hashes"); // Format: hashname = filename.png - for (std::string hashName : hashNames) { + for (const std::string &hashName : hashNames) { hashes->Get(hashName.c_str(), &aliases_[hashName], ""); } } @@ -104,7 +104,7 @@ bool TextureReplacer::LoadIni() { if (ini.GetKeys("hashranges", hashrangeKeys)) { auto hashranges = ini.GetOrCreateSection("hashranges"); // Format: addr,w,h = newW,newH - for (std::string key : hashrangeKeys) { + for (const std::string &key : hashrangeKeys) { std::string value; if (hashranges->Get(key.c_str(), &value, "")) { ParseHashRange(key, value); @@ -277,7 +277,7 @@ static bool WriteTextureToPNG(png_imagep image, const std::string &filename, int } #endif -void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, ReplacedTextureFormat fmt) { +void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, int scaleFactor, ReplacedTextureFormat fmt) { _assert_msg_(G3D, enabled_, "Replacement not enabled"); if (!g_Config.bSaveNewTextures) { // Ignore. @@ -308,7 +308,12 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con } // Only save the hashed portion of the PNG. - LookupHashRange(addr, w, h); + int lookupW = w / scaleFactor; + int lookupH = h / scaleFactor; + if (LookupHashRange(addr, lookupW, lookupH)) { + w = lookupW * scaleFactor; + h = lookupH * scaleFactor; + } #ifdef USING_QT_UI ERROR_LOG(G3D, "Replacement texture saving not implemented for Qt"); diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index f9cc69dd3934..13d16bc873e2 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -133,7 +133,7 @@ class TextureReplacer { ReplacedTexture &FindReplacement(u64 cachekey, u32 hash, int w, int h); - void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, ReplacedTextureFormat fmt); + void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, int scaleFactor, ReplacedTextureFormat fmt); protected: bool LoadIni(); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 8133b508a6b9..9b5f5cde0aa2 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -1674,7 +1674,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re if (replacer.Enabled()) { int bpp = dstFmt == D3DFMT_A8R8G8B8 ? 4 : 2; - replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash, entry.addr, pixelData, w * bpp, level, w, h, FromD3D9Format(dstFmt)); + replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash, entry.addr, pixelData, w * bpp, level, w, h, scaleFactor, FromD3D9Format(dstFmt)); } } diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index d177a4f15448..a1dcd4fedf4a 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -1813,7 +1813,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &repla if (replacer.Enabled()) { int bpp = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; - replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash,entry.addr, pixelData, (useUnpack ? bufw : w) * bpp, level, w, h, FromGLESFormat(dstFmt, useBGRA)); + replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash,entry.addr, pixelData, (useUnpack ? bufw : w) * bpp, level, w, h, scaleFactor, FromGLESFormat(dstFmt, useBGRA)); } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index a1418201f279..3e25e66cf268 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1348,7 +1348,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { } else { LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); if (replacer.Enabled()) { - replacer.NotifyTextureDecoded(cachekey, entry->fullhash, texaddr, data, stride, i, mipWidth, mipHeight, FromVulkanFormat(actualFmt)); + replacer.NotifyTextureDecoded(cachekey, entry->fullhash, texaddr, data, stride, i, mipWidth, mipHeight, scaleFactor, FromVulkanFormat(actualFmt)); } } entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); From 223f95f28ce31f8a24137bcd732a0e2d5729ed6f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 20:55:30 -0700 Subject: [PATCH 21/34] Allow a short alias for video frames, etc. --- Core/TextureReplacer.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 51e08cb22d34..3e3520794f23 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -384,6 +384,14 @@ std::string TextureReplacer::LookupHashFile(u64 cachekey, u32 hash, int level) { return alias->second; } + // Also check for a cachekey-only alias. This is mainly for ignoring videos. + const std::string keyonly = hashname.substr(0, 16); + auto keyonlyAlias = aliases_.find(keyonly); + if (keyonlyAlias != aliases_.end()) { + // Note: this will be blank if explicitly ignored. + return keyonlyAlias->second; + } + return hashname + ".png"; } From bed82da3526515ba1b2487f1ad45a5d85da321ed Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 21:10:48 -0700 Subject: [PATCH 22/34] Make ini case insensitive. --- Core/TextureReplacer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 3e3520794f23..99cf4fcff54a 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -19,6 +19,7 @@ #include #endif +#include #include "ext/xxhash.h" #include "file/ini_file.h" #include "Common/ColorConv.h" @@ -79,7 +80,7 @@ bool TextureReplacer::LoadIni() { std::string hash; options->Get("hash", &hash, ""); // TODO: crc32c. - if (hash == "quick") { + if (strcasecmp(hash.c_str(), "quick") == 0) { hash_ = ReplacedTextureHash::QUICK; } else { ERROR_LOG(G3D, "Unsupported hash type: %s", hash.c_str()); @@ -95,7 +96,8 @@ bool TextureReplacer::LoadIni() { if (ini.GetKeys("hashes", hashNames)) { auto hashes = ini.GetOrCreateSection("hashes"); // Format: hashname = filename.png - for (const std::string &hashName : hashNames) { + for (std::string hashName : hashNames) { + std::transform(hashName.begin(), hashName.end(), hashName.begin(), tolower); hashes->Get(hashName.c_str(), &aliases_[hashName], ""); } } From c4b27525e0d37354635f2c3dd9d65f7ebed724b5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 22:09:07 -0700 Subject: [PATCH 23/34] Disable texture replacement on Qt. --- Core/TextureReplacer.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 99cf4fcff54a..c0febc3563ec 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -237,6 +237,9 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, level.fmt = ReplacedTextureFormat::F_8888; level.file = filename; +#ifdef USING_QT_UI + ERROR_LOG(G3D, "Replacement texture loading not implemented for Qt"); +#else png_image png = {}; png.version = PNG_IMAGE_VERSION; FILE *fp = File::OpenCFile(filename, "rb"); @@ -251,6 +254,7 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, } fclose(fp); png_image_free(&png); +#endif } // TODO: Could calculate alpha status, or maybe from ini? Let's ignore for now. @@ -427,6 +431,9 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) { const ReplacedTextureLevel &info = levels_[level]; +#ifdef USING_QT_UI + ERROR_LOG(G3D, "Replacement texture loading not implemented for Qt"); +#else png_image png = {}; png.version = PNG_IMAGE_VERSION; png.format = PNG_FORMAT_RGBA; @@ -443,4 +450,5 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) { fclose(fp); png_image_free(&png); +#endif } From f26c0328da9f1710c9532d6a1dd0840b71bfce5b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 23:58:58 -0700 Subject: [PATCH 24/34] Check alpha when loading replaced textures. --- Core/TextureReplacer.cpp | 15 ++++++++++----- GPU/Directx9/TextureCacheDX9.cpp | 2 +- GPU/GLES/TextureCache.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index c0febc3563ec..e06dd6a5e2a7 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -250,14 +250,13 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, result->levels_.push_back(level); } else { - ERROR_LOG(G3D, "Could not load texture replacement info: %s", filename.c_str()); + ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s", filename.c_str(), png.message); } fclose(fp); + png_image_free(&png); #endif } - - // TODO: Could calculate alpha status, or maybe from ini? Let's ignore for now. } #ifndef USING_QT_UI @@ -440,14 +439,20 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) { FILE *fp = File::OpenCFile(info.file, "rb"); if (!png_image_begin_read_from_stdio(&png, fp)) { - ERROR_LOG(G3D, "Could not load texture replacement info: %s", info.file.c_str()); + ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s", info.file.c_str(), png.message); return; } if (!png_image_finish_read(&png, nullptr, out, rowPitch, nullptr)) { - ERROR_LOG(G3D, "Could not load texture replacement: %s", info.file.c_str()); + ERROR_LOG(G3D, "Could not load texture replacement: %s - %s", info.file.c_str(), png.message); return; } + if (level == 0) { + // This will only check the hashed bits. + CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)out, rowPitch / sizeof(u32), png.width, png.height); + alphaStatus_ = ReplacedTextureAlpha(res); + } + fclose(fp); png_image_free(&png); #endif diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 9b5f5cde0aa2..e9478844c19b 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -1255,7 +1255,7 @@ void TextureCacheDX9::SetTexture(bool force) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); + ReplacedTexture &replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index a1dcd4fedf4a..ed8cf6322c3d 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -1364,7 +1364,7 @@ void TextureCache::SetTexture(bool force) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); + ReplacedTexture &replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 3e25e66cf268..54fd164d6fbf 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1237,7 +1237,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1); } - ReplacedTexture replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); + ReplacedTexture &replaced = replacer.FindReplacement(cachekey, entry->fullhash, w, h); if (replaced.GetSize(0, w, h)) { // We're replacing, so we won't scale. scaleFactor = 1; From 23ab2cd187d82e95f1b579b7002198f7b158e9d8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Apr 2016 23:59:17 -0700 Subject: [PATCH 25/34] Vulkan: Correct check alpha. --- GPU/Vulkan/TextureCacheVulkan.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 54fd164d6fbf..60194bd11232 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1634,13 +1634,13 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt // We always end up at 8888. Other parts assume this. assert(dstFmt == VULKAN_8888_FORMAT); - decPitch = w * sizeof(u32); - rowBytes = w * sizeof(u32); + bpp = sizeof(u32); + decPitch = w * bpp; + rowBytes = w * bpp; } if ((entry.status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { - // TODO: bufw may be wrong. - TexCacheEntry::Status alphaStatus = CheckAlpha(pixelData, dstFmt, bufw, w, h); + TexCacheEntry::Status alphaStatus = CheckAlpha(pixelData, dstFmt, decPitch / bpp, w, h); entry.SetAlphaStatus(alphaStatus, level); } else { entry.SetAlphaStatus(TexCacheEntry::STATUS_ALPHA_UNKNOWN); From cd6f36a43993642cefce91e8b47ba6a5d0a819eb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 00:20:58 -0700 Subject: [PATCH 26/34] Correct handling of 8 bit and no-alpha PNGs. --- Core/TextureReplacer.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index e06dd6a5e2a7..742f9d1b4942 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -435,19 +435,27 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) { #else png_image png = {}; png.version = PNG_IMAGE_VERSION; - png.format = PNG_FORMAT_RGBA; FILE *fp = File::OpenCFile(info.file, "rb"); if (!png_image_begin_read_from_stdio(&png, fp)) { ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s", info.file.c_str(), png.message); return; } + + bool checkedAlpha = false; + if ((png.format & PNG_FORMAT_FLAG_ALPHA) == 0) { + // Well, we know for sure it doesn't have alpha. + alphaStatus_ = ReplacedTextureAlpha::FULL; + checkedAlpha = true; + } + png.format = PNG_FORMAT_RGBA; + if (!png_image_finish_read(&png, nullptr, out, rowPitch, nullptr)) { ERROR_LOG(G3D, "Could not load texture replacement: %s - %s", info.file.c_str(), png.message); return; } - if (level == 0) { + if (level == 0 && !checkedAlpha) { // This will only check the hashed bits. CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)out, rowPitch / sizeof(u32), png.width, png.height); alphaStatus_ = ReplacedTextureAlpha(res); From 450554450ff7fbde3c82c61f9ec602a4a6708b28 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 00:33:37 -0700 Subject: [PATCH 27/34] Attempt to buildfix Symbian. --- Core/TextureReplacer.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 13d16bc873e2..190c0f0b06d2 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include #include @@ -68,8 +69,16 @@ struct ReplacementCacheKey { bool operator ==(const ReplacementCacheKey &k) const { return k.cachekey == cachekey && k.hash == hash; } + + bool operator <(const ReplacementCacheKey &k) const { + if (k.cachekey == cachekey) { + return k.hash < hash; + } + return k.cachekey < cachekey; + } }; +#ifndef __SYMBIAN32__ namespace std { template <> struct hash { @@ -78,6 +87,7 @@ namespace std { } }; } +#endif struct ReplacedTexture { inline bool Valid() { @@ -150,9 +160,18 @@ class TextureReplacer { ReplacedTextureHash hash_; std::unordered_map aliases_; typedef std::pair WidthHeightPair; +#ifdef __SYMBIAN32__ + std::map hashranges_; +#else std::unordered_map hashranges_; +#endif ReplacedTexture none_; +#ifdef __SYMBIAN32__ + std::map cache_; + std::map savedCache_; +#else std::unordered_map cache_; std::unordered_map savedCache_; +#endif }; From 329be68f574d1d09c1ae1eaaf4a4d1946330aafb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 08:04:15 -0700 Subject: [PATCH 28/34] Fix error when disabled. We want to return a reference here, so let's always return. --- Core/TextureReplacer.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 742f9d1b4942..391a4776edd3 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -200,10 +200,8 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, GETextureForm } ReplacedTexture &TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w, int h) { - _assert_msg_(G3D, enabled_, "Replacement not enabled"); - // Only actually replace if we're replacing. We might just be saving. - if (!g_Config.bReplaceTextures) { + if (!Enabled() || !g_Config.bReplaceTextures) { return none_; } From 99d29356d7bc999f16fd3ce59bc36a00be8669b6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 08:39:18 -0700 Subject: [PATCH 29/34] Track video addresses in texture cache. --- GPU/Common/TextureCacheCommon.cpp | 19 +++++++++++++++++++ GPU/Common/TextureCacheCommon.h | 5 +++++ GPU/Directx9/GPU_DX9.cpp | 1 + GPU/Directx9/TextureCacheDX9.cpp | 3 +++ GPU/GLES/GPU_GLES.cpp | 1 + GPU/GLES/TextureCache.cpp | 3 +++ GPU/Vulkan/GPU_Vulkan.cpp | 7 ++++++- GPU/Vulkan/TextureCacheVulkan.cpp | 3 +++ 8 files changed, 41 insertions(+), 1 deletion(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 67345cd33a79..3cd700732ead 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -33,6 +33,9 @@ #include #endif +// Videos should be updated every few frames, so we forge quickly. +#define VIDEO_DECIMATE_AGE 4 + // Ugly. extern int g_iNumVideos; @@ -145,6 +148,18 @@ void TextureCacheCommon::UpdateMaxSeenV(bool throughMode) { } } +void TextureCacheCommon::DecimateVideos() { + if (!videos_.empty()) { + for (auto iter = videos_.begin(); iter != videos_.end(); ) { + if (iter->second + VIDEO_DECIMATE_AGE < gpuStats.numFlips) { + videos_.erase(iter++); + } else { + ++iter; + } + } + } +} + void TextureCacheCommon::NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer, FramebufferNotification msg) { // Must be in VRAM so | 0x04000000 it is. Also, ignore memory mirrors. // These checks are mainly to reduce scanning all textures. @@ -293,6 +308,10 @@ void TextureCacheCommon::NotifyConfigChanged() { replacer.NotifyConfigChanged(); } +void TextureCacheCommon::NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt) { + videos_[addr] = gpuStats.numFlips; +} + void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { clutTotalBytes_ = loadBytes; clutRenderAddress_ = 0xFFFFFFFF; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 6dcec60e5b56..8e6b5bd1102d 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -55,6 +55,7 @@ class TextureCacheCommon { // FramebufferManager keeps TextureCache updated about what regions of memory are being rendered to. void NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer, FramebufferNotification msg); void NotifyConfigChanged(); + void NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt); int AttachedDrawingHeight(); @@ -155,6 +156,8 @@ class TextureCacheCommon { virtual void DownloadFramebufferForClut(u32 clutAddr, u32 bytes) = 0; + void DecimateVideos(); + TextureReplacer replacer; TexCache cache; @@ -171,6 +174,8 @@ class TextureCacheCommon { void AttachFramebufferInvalid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const AttachedFramebufferInfo &fbInfo); void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer); + std::map videos_; + SimpleBuf tmpTexBuf32; SimpleBuf tmpTexBuf16; SimpleBuf tmpTexBufRearrange; diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 190c99d6d992..9e29ad3dd8db 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -2006,6 +2006,7 @@ void GPU_DX9::NotifyVideoUpload(u32 addr, int size, int width, int format) { if (Memory::IsVRAMAddress(addr)) { framebufferManager_.NotifyVideoUpload(addr, size, width, (GEBufferFormat)format); } + textureCache_.NotifyVideoUpload(addr, size, width, (GEBufferFormat)format); InvalidateCache(addr, size, GPU_INVALIDATE_SAFE); } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index e9478844c19b..af170647f33b 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -112,6 +112,7 @@ void TextureCacheDX9::Clear(bool delete_them) { secondCacheSizeEstimate_ = 0; } fbTexInfo_.clear(); + videos_.clear(); } void TextureCacheDX9::DeleteTexture(TexCache::iterator it) { @@ -170,6 +171,8 @@ void TextureCacheDX9::Decimate() { VERBOSE_LOG(G3D, "Decimated second texture cache, saved %d estimated bytes - now %d bytes", had - secondCacheSizeEstimate_, secondCacheSizeEstimate_); } + + DecimateVideos(); } void TextureCacheDX9::Invalidate(u32 addr, int size, GPUInvalidationType type) { diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 3324f8f815ec..b542631a0a12 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -2261,6 +2261,7 @@ void GPU_GLES::NotifyVideoUpload(u32 addr, int size, int width, int format) { if (Memory::IsVRAMAddress(addr)) { framebufferManager_.NotifyVideoUpload(addr, size, width, (GEBufferFormat)format); } + textureCache_.NotifyVideoUpload(addr, size, width, (GEBufferFormat)format); InvalidateCache(addr, size, GPU_INVALIDATE_SAFE); } diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index ed8cf6322c3d..876276927a81 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -114,6 +114,7 @@ void TextureCache::Clear(bool delete_them) { secondCacheSizeEstimate_ = 0; } fbTexInfo_.clear(); + videos_.clear(); } void TextureCache::DeleteTexture(TexCache::iterator it) { @@ -168,6 +169,8 @@ void TextureCache::Decimate() { VERBOSE_LOG(G3D, "Decimated second texture cache, saved %d estimated bytes - now %d bytes", had - secondCacheSizeEstimate_, secondCacheSizeEstimate_); } + + DecimateVideos(); } void TextureCache::Invalidate(u32 addr, int size, GPUInvalidationType type) { diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 78111a790aea..6f91e2ec7c22 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -2167,7 +2167,12 @@ bool GPU_Vulkan::PerformMemorySet(u32 dest, u8 v, int size) { } void GPU_Vulkan::NotifyVideoUpload(u32 addr, int size, int width, int format) { - + if (Memory::IsVRAMAddress(addr)) { + // TODO + //framebufferManager_.NotifyVideoUpload(addr, size, width, (GEBufferFormat)format); + } + textureCache_.NotifyVideoUpload(addr, size, width, (GEBufferFormat)format); + InvalidateCache(addr, size, GPU_INVALIDATE_SAFE); } bool GPU_Vulkan::PerformMemoryDownload(u32 dest, int size) { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 60194bd11232..3f6741ced376 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -177,6 +177,7 @@ void TextureCacheVulkan::Clear(bool delete_them) { secondCacheSizeEstimate_ = 0; } fbTexInfo_.clear(); + videos_.clear(); } void TextureCacheVulkan::DeleteTexture(TexCache::iterator it) { @@ -230,6 +231,8 @@ void TextureCacheVulkan::Decimate() { VERBOSE_LOG(G3D, "Decimated second texture cache, saved %d estimated bytes - now %d bytes", had - secondCacheSizeEstimate_, secondCacheSizeEstimate_); } + + DecimateVideos(); } void TextureCacheVulkan::Invalidate(u32 addr, int size, GPUInvalidationType type) { From f5b93bc6f059daa803531167a170f172d0ddf557 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 08:53:48 -0700 Subject: [PATCH 30/34] Remove global num videos hack. --- Core/HW/MediaEngine.cpp | 4 ---- GPU/Common/TextureCacheCommon.cpp | 15 ++++++++------- GPU/Common/TextureCacheCommon.h | 2 +- GPU/Directx9/TextureCacheDX9.cpp | 6 ++---- GPU/GLES/Framebuffer.cpp | 2 -- GPU/GLES/TextureCache.cpp | 7 ++----- GPU/Vulkan/FramebufferVulkan.cpp | 2 -- GPU/Vulkan/TextureCacheVulkan.cpp | 7 ++----- 8 files changed, 15 insertions(+), 30 deletions(-) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index da4140b88b75..a927a9c3bda9 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -39,8 +39,6 @@ extern "C" { } #endif // USE_FFMPEG -int g_iNumVideos = 0; - #ifdef USE_FFMPEG static AVPixelFormat getSwsFormat(int pspFormat) { @@ -149,12 +147,10 @@ MediaEngine::MediaEngine(): m_pdata(0) { m_ringbuffersize = 0; m_mpegheaderReadPos = 0; m_audioType = PSP_CODEC_AT3PLUS; // in movie, we use only AT3+ audio - g_iNumVideos++; } MediaEngine::~MediaEngine() { closeMedia(); - g_iNumVideos--; } void MediaEngine::closeMedia() { diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 3cd700732ead..4f423d4604ad 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -36,9 +36,6 @@ // Videos should be updated every few frames, so we forge quickly. #define VIDEO_DECIMATE_AGE 4 -// Ugly. -extern int g_iNumVideos; - TextureCacheCommon::TextureCacheCommon() : cacheSizeEstimate_(0), nextTexture_(nullptr), clutLastFormat_(0xFFFFFFFF), clutTotalBytes_(0), clutMaxBytes_(0), clutRenderAddress_(0xFFFFFFFF) { @@ -79,7 +76,7 @@ int TextureCacheCommon::AttachedDrawingHeight() { return 0; } -void TextureCacheCommon::GetSamplingParams(int &minFilt, int &magFilt, bool &sClamp, bool &tClamp, float &lodBias, u8 maxLevel) { +void TextureCacheCommon::GetSamplingParams(int &minFilt, int &magFilt, bool &sClamp, bool &tClamp, float &lodBias, u8 maxLevel, u32 addr) { minFilt = gstate.texfilter & 0x7; magFilt = (gstate.texfilter >> 8) & 1; sClamp = gstate.isTexCoordClampedS(); @@ -96,9 +93,12 @@ void TextureCacheCommon::GetSamplingParams(int &minFilt, int &magFilt, bool &sCl lodBias = (float)(int)(s8)((gstate.texlevel >> 16) & 0xFF) / 16.0f; } - if (g_Config.iTexFiltering == TEX_FILTER_LINEAR_VIDEO && g_iNumVideos > 0 && (gstate.getTextureDimension(0) & 0xF) >= 9) { - magFilt |= 1; - minFilt |= 1; + if (g_Config.iTexFiltering == TEX_FILTER_LINEAR_VIDEO) { + bool isVideo = videos_.find(addr & 0x3FFFFFFF) != videos_.end(); + if (isVideo) { + magFilt |= 1; + minFilt |= 1; + } } if (g_Config.iTexFiltering == TEX_FILTER_LINEAR && (!gstate.isColorTestEnabled() || IsColorTestTriviallyTrue())) { if (!gstate.isAlphaTestEnabled() || IsAlphaTestTriviallyTrue()) { @@ -309,6 +309,7 @@ void TextureCacheCommon::NotifyConfigChanged() { } void TextureCacheCommon::NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt) { + addr &= 0x3FFFFFFF; videos_[addr] = gpuStats.numFlips; } diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 8e6b5bd1102d..bf8e11e7d6b6 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -149,7 +149,7 @@ class TextureCacheCommon { void *RearrangeBuf(void *inBuf, u32 inRowBytes, u32 outRowBytes, int h, bool allowInPlace = true); u32 EstimateTexMemoryUsage(const TexCacheEntry *entry); - void GetSamplingParams(int &minFilt, int &magFilt, bool &sClamp, bool &tClamp, float &lodBias, u8 maxLevel); + void GetSamplingParams(int &minFilt, int &magFilt, bool &sClamp, bool &tClamp, float &lodBias, u8 maxLevel, u32 addr); void UpdateMaxSeenV(bool throughMode); virtual bool AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset = 0) = 0; diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index af170647f33b..92d12ecb71df 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -38,8 +38,6 @@ #include "math/math_util.h" -extern int g_iNumVideos; - namespace DX9 { #define INVALID_TEX (LPDIRECT3DTEXTURE9)(-1) @@ -503,7 +501,7 @@ void TextureCacheDX9::UpdateSamplingParams(TexCacheEntry &entry, bool force) { bool sClamp; bool tClamp; float lodBias; - GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, entry.maxLevel); + GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, entry.maxLevel, entry.addr); if (entry.maxLevel != 0) { GETexLevelMode mode = gstate.getTexLevelMode(); @@ -543,7 +541,7 @@ void TextureCacheDX9::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHe bool sClamp; bool tClamp; float lodBias; - GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0); + GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0, 0); dxstate.texMinFilter.set(MinFilt[minFilt]); dxstate.texMipFilter.set(MipFilt[minFilt]); diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index b7d10993c0a4..8ecce9e131ef 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -50,8 +50,6 @@ // #define DEBUG_READ_PIXELS 1 -extern int g_iNumVideos; - static const char tex_fs[] = #ifdef USING_GLES2 "precision mediump float;\n" diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 876276927a81..1bff2b8014f0 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -71,9 +71,6 @@ #define INVALID_TEX -1 -// Hack! -extern int g_iNumVideos; - TextureCache::TextureCache() : secondCacheSizeEstimate_(0), clearCacheNextFrame_(false), lowMemoryMode_(false), clutBuf_(NULL), texelsScaledThisFrame_(0) { timesInvalidatedAllThisFrame_ = 0; lastBoundTexture = INVALID_TEX; @@ -490,7 +487,7 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { bool sClamp; bool tClamp; float lodBias; - GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, entry.maxLevel); + GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, entry.maxLevel, entry.addr); if (entry.maxLevel != 0) { if (force || entry.lodBias != lodBias) { @@ -544,7 +541,7 @@ void TextureCache::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeigh bool sClamp; bool tClamp; float lodBias; - GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0); + GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0, 0); minFilt &= 1; // framebuffers can't mipmap. diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 64ab3865ad2c..5fefbc7c3ea2 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -52,8 +52,6 @@ #include "UI/OnScreenDisplay.h" -extern int g_iNumVideos; - const VkFormat framebufFormat = VK_FORMAT_R8G8B8A8_UNORM; static const char tex_fs[] = R"(#version 400 diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 3f6741ced376..359e78f87ca5 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -83,9 +83,6 @@ static const VkComponentMapping VULKAN_1555_SWIZZLE = { VK_COMPONENT_SWIZZLE_B, static const VkComponentMapping VULKAN_565_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; static const VkComponentMapping VULKAN_8888_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; -// Hack! -extern int g_iNumVideos; - SamplerCache::~SamplerCache() { for (auto iter : cache_) { vulkan_->Delete().QueueDeleteSampler(iter.second); @@ -508,7 +505,7 @@ void TextureCacheVulkan::UpdateSamplingParams(TexCacheEntry &entry, SamplerCache bool sClamp; bool tClamp; float lodBias; - GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, entry.maxLevel); + GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, entry.maxLevel, entry.addr); key.minFilt = minFilt & 1; key.mipEnable = (minFilt >> 2) & 1; key.mipFilt = (minFilt >> 1) & 1; @@ -549,7 +546,7 @@ void TextureCacheVulkan::SetFramebufferSamplingParams(u16 bufferWidth, u16 buffe bool sClamp; bool tClamp; float lodBias; - GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0); + GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0, 0); key.minFilt = minFilt & 1; key.mipFilt = 0; From c20075b0e0ff8896fa7e8fb2fa3757f17f3f93f5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 08:54:43 -0700 Subject: [PATCH 31/34] Pass video info to texture replacements. --- Core/TextureReplacer.cpp | 26 +++++++++++++------------- Core/TextureReplacer.h | 12 +++++++++++- GPU/Directx9/TextureCacheDX9.cpp | 11 ++++++++++- GPU/GLES/TextureCache.cpp | 11 ++++++++++- GPU/Vulkan/TextureCacheVulkan.cpp | 13 ++++++++++++- 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 391a4776edd3..1461c0cb4ab8 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -280,18 +280,18 @@ static bool WriteTextureToPNG(png_imagep image, const std::string &filename, int } #endif -void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, int scaleFactor, ReplacedTextureFormat fmt) { +void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h) { _assert_msg_(G3D, enabled_, "Replacement not enabled"); if (!g_Config.bSaveNewTextures) { // Ignore. return; } - if (addr > 0x05000000 && addr < 0x08800000) { + if (replacedInfo.addr > 0x05000000 && replacedInfo.addr < 0x08800000) { // Don't save the PPGe texture. return; } - std::string hashfile = LookupHashFile(cachekey, hash, level); + std::string hashfile = LookupHashFile(replacedInfo.cachekey, replacedInfo.hash, level); const std::string filename = basePath_ + hashfile; const std::string saveFilename = basePath_ + NEW_TEXTURE_DIR + hashfile; @@ -301,7 +301,7 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con return; } - ReplacementCacheKey replacementKey(cachekey, hash); + ReplacementCacheKey replacementKey(replacedInfo.cachekey, replacedInfo.hash); auto it = savedCache_.find(replacementKey); if (it != savedCache_.end() && File::Exists(saveFilename)) { // We've already saved this texture. Let's only save if it's bigger (e.g. scaled now.) @@ -311,19 +311,19 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con } // Only save the hashed portion of the PNG. - int lookupW = w / scaleFactor; - int lookupH = h / scaleFactor; - if (LookupHashRange(addr, lookupW, lookupH)) { - w = lookupW * scaleFactor; - h = lookupH * scaleFactor; + int lookupW = w / replacedInfo.scaleFactor; + int lookupH = h / replacedInfo.scaleFactor; + if (LookupHashRange(replacedInfo.addr, lookupW, lookupH)) { + w = lookupW * replacedInfo.scaleFactor; + h = lookupH * replacedInfo.scaleFactor; } #ifdef USING_QT_UI ERROR_LOG(G3D, "Replacement texture saving not implemented for Qt"); #else - if (fmt != ReplacedTextureFormat::F_8888) { + if (replacedInfo.fmt != ReplacedTextureFormat::F_8888) { saveBuf.resize((pitch * h) / sizeof(u16)); - switch (fmt) { + switch (replacedInfo.fmt) { case ReplacedTextureFormat::F_5650: ConvertRGBA565ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16)); break; @@ -348,7 +348,7 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con } data = saveBuf.data(); - if (fmt != ReplacedTextureFormat::F_8888_BGRA) { + if (replacedInfo.fmt != ReplacedTextureFormat::F_8888_BGRA) { // We doubled our pitch. pitch *= 2; } @@ -366,7 +366,7 @@ void TextureReplacer::NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, con if (png.warning_or_error >= 2) { ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors."); } else if (success) { - NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", hash, w, h); + NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", replacedInfo.hash, w, h); } #endif diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 190c0f0b06d2..49a82d5e2cc6 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -127,6 +127,16 @@ struct ReplacedTexture { friend TextureReplacer; }; +struct ReplacedTextureDecodeInfo { + u64 cachekey; + u32 hash; + u32 addr; + bool isVideo; + bool isFinal; + int scaleFactor; + ReplacedTextureFormat fmt; +}; + class TextureReplacer { public: TextureReplacer(); @@ -143,7 +153,7 @@ class TextureReplacer { ReplacedTexture &FindReplacement(u64 cachekey, u32 hash, int w, int h); - void NotifyTextureDecoded(u64 cachekey, u32 hash, u32 addr, const void *data, int pitch, int level, int w, int h, int scaleFactor, ReplacedTextureFormat fmt); + void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h); protected: bool LoadIni(); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 92d12ecb71df..dae0ff03b848 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -1674,8 +1674,17 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re } if (replacer.Enabled()) { + ReplacedTextureDecodeInfo replacedInfo; + replacedInfo.cachekey = entry.CacheKey(); + replacedInfo.hash = entry.fullhash; + replacedInfo.addr = entry.addr; + replacedInfo.isVideo = videos_.find(entry.addr & 0x3FFFFFFF) != videos_.end(); + replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = scaleFactor; + replacedInfo.fmt = FromD3D9Format(dstFmt); + int bpp = dstFmt == D3DFMT_A8R8G8B8 ? 4 : 2; - replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash, entry.addr, pixelData, w * bpp, level, w, h, scaleFactor, FromD3D9Format(dstFmt)); + replacer.NotifyTextureDecoded(replacedInfo, pixelData, w * bpp, level, w, h); } } diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 1bff2b8014f0..09bc84957dbc 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -1812,8 +1812,17 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &repla } if (replacer.Enabled()) { + ReplacedTextureDecodeInfo replacedInfo; + replacedInfo.cachekey = entry.CacheKey(); + replacedInfo.hash = entry.fullhash; + replacedInfo.addr = entry.addr; + replacedInfo.isVideo = videos_.find(entry.addr & 0x3FFFFFFF) != videos_.end(); + replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = scaleFactor; + replacedInfo.fmt = FromGLESFormat(dstFmt, useBGRA); + int bpp = dstFmt == GL_UNSIGNED_BYTE ? 4 : 2; - replacer.NotifyTextureDecoded(entry.CacheKey(), entry.fullhash,entry.addr, pixelData, (useUnpack ? bufw : w) * bpp, level, w, h, scaleFactor, FromGLESFormat(dstFmt, useBGRA)); + replacer.NotifyTextureDecoded(replacedInfo, pixelData, (useUnpack ? bufw : w) * bpp, level, w, h); } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 359e78f87ca5..ca20481862bc 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1329,6 +1329,17 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { } lastBoundTexture = entry->vkTex; + ReplacedTextureDecodeInfo replacedInfo; + if (replacer.Enabled() && !replaced.Valid()) { + replacedInfo.cachekey = cachekey; + replacedInfo.hash = entry->fullhash; + replacedInfo.addr = texaddr; + replacedInfo.isVideo = videos_.find(texaddr & 0x3FFFFFFF) != videos_.end(); + replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = scaleFactor; + replacedInfo.fmt = FromVulkanFormat(actualFmt); + } + if (entry->vkTex) { // Upload the texture data. for (int i = 0; i <= maxLevel; i++) { @@ -1348,7 +1359,7 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) { } else { LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); if (replacer.Enabled()) { - replacer.NotifyTextureDecoded(cachekey, entry->fullhash, texaddr, data, stride, i, mipWidth, mipHeight, scaleFactor, FromVulkanFormat(actualFmt)); + replacer.NotifyTextureDecoded(replacedInfo, data, stride, i, mipWidth, mipHeight); } } entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); From 994d2dd85e5d43761ae94e5738865502fa0e4e39 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 08:58:14 -0700 Subject: [PATCH 32/34] Skip video in replacement saving by default. --- Core/TextureReplacer.cpp | 7 ++++++- Core/TextureReplacer.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 1461c0cb4ab8..4da568218e26 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -35,7 +35,7 @@ static const std::string NEW_TEXTURE_DIR = "new/"; static const int VERSION = 1; static const int MAX_MIP_LEVELS = 64; -TextureReplacer::TextureReplacer() : enabled_(false) { +TextureReplacer::TextureReplacer() : enabled_(false), allowVideo_(false), hash_(ReplacedTextureHash::QUICK) { none_.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; } @@ -87,6 +87,8 @@ bool TextureReplacer::LoadIni() { return false; } + options->Get("video", &allowVideo_, false); + int version = 0; if (options->Get("version", &version, 0) && version > VERSION) { ERROR_LOG(G3D, "Unsupported texture replacement version %d, trying anyway", version); @@ -290,6 +292,9 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl // Don't save the PPGe texture. return; } + if (replacedInfo.isVideo && !allowVideo_) { + return; + } std::string hashfile = LookupHashFile(replacedInfo.cachekey, replacedInfo.hash, level); const std::string filename = basePath_ + hashfile; diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 49a82d5e2cc6..d90d484e28b5 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -165,6 +165,7 @@ class TextureReplacer { SimpleBuf saveBuf; bool enabled_; + bool allowVideo_; std::string gameID_; std::string basePath_; ReplacedTextureHash hash_; From c30287c4e58f00ca73e03c6650594df7658c1223 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 09:01:14 -0700 Subject: [PATCH 33/34] Another buildfix for Qt, linking issue. This is a bit of a hack, but it resolves the linking issue that only seems to happen in the Qt build system. --- Qt/GPU.pro | 3 ++- Qt/Settings.pri | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Qt/GPU.pro b/Qt/GPU.pro index e993b64b5a54..63be849c9078 100644 --- a/Qt/GPU.pro +++ b/Qt/GPU.pro @@ -61,7 +61,8 @@ SOURCES += $$P/GPU/GeDisasm.cpp \ # GPU $$P/GPU/Common/SplineCommon.cpp \ $$P/GPU/Common/DrawEngineCommon.cpp \ $$P/ext/xxhash.c \ # xxHash - $$P/ext/xbrz/*.cpp # XBRZ + $$P/ext/xbrz/*.cpp \ # XBRZ + $$P/Core/TextureReplacer.cpp # Bit of a hack. Avoids a linking issue. armv7: SOURCES += $$P/GPU/Common/TextureDecoderNEON.cpp diff --git a/Qt/Settings.pri b/Qt/Settings.pri index 83405099dcd3..0f731dec7d79 100644 --- a/Qt/Settings.pri +++ b/Qt/Settings.pri @@ -67,6 +67,11 @@ win32-msvc* { QMAKE_ALLFLAGS_RELEASE += -O3 -ffast-math } +symbian { + # Silence a common warning in system headers. + QMAKE_CXXFLAGS += -Wno-address +} + contains(QT_CONFIG, opengles.) { DEFINES += USING_GLES2 # How else do we know if the environment prefers windows? From 2e1986d0c88b47a393e66f3ceccba8b07ea6caf0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 May 2016 09:50:03 -0700 Subject: [PATCH 34/34] Fix a few reorder warnings. --- Core/HLE/sceAtrac.cpp | 2 +- Core/HW/SasReverb.cpp | 2 +- GPU/Common/VertexDecoderCommon.cpp | 2 +- ext/native/ui/ui_screen.cpp | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 3df69bbfe145..b6f05d8f39a3 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -176,7 +176,7 @@ struct Atrac { channels_(0), outputChannels_(2), bitrate_(64), bytesPerFrame_(0), bufferMaxSize_(0), jointStereo_(0), currentSample_(0), endSample_(0), firstSampleOffset_(0), dataOff_(0), loopStartSample_(-1), loopEndSample_(-1), loopNum_(0), - failedDecode_(false), codecType_(0), ignoreDataBuf_(false), + failedDecode_(false), ignoreDataBuf_(false), codecType_(0), bufferState_(ATRAC_STATUS_NO_DATA) { memset(&first_, 0, sizeof(first_)); memset(&second_, 0, sizeof(second_)); diff --git a/Core/HW/SasReverb.cpp b/Core/HW/SasReverb.cpp index c1d148342c7d..fd9f592b8be0 100644 --- a/Core/HW/SasReverb.cpp +++ b/Core/HW/SasReverb.cpp @@ -182,7 +182,7 @@ void SasReverb::SetPreset(int preset) { template class BufferWrapper { public: - BufferWrapper(int16_t *buffer, int position, int usedSize) : buf_(buffer), pos_(position), base_(bufsize - usedSize), end_(bufsize), size_(usedSize) {} + BufferWrapper(int16_t *buffer, int position, int usedSize) : buf_(buffer), pos_(position), end_(bufsize), base_(bufsize - usedSize), size_(usedSize) {} int16_t &operator [](int index) { int addr = pos_ + index; if (addr >= end_) { addr -= size_; } diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 0d58cde4ad8f..075626d140d7 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -152,7 +152,7 @@ void PrintDecodedVertex(VertexReader &vtx) { printf("P: %f %f %f\n", pos[0], pos[1], pos[2]); } -VertexDecoder::VertexDecoder() : jitted_(0), jittedSize_(0), decoded_(nullptr), ptr_(nullptr) { +VertexDecoder::VertexDecoder() : decoded_(nullptr), ptr_(nullptr), jitted_(0), jittedSize_(0) { } void VertexDecoder::Step_WeightsU8() const diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index 55a7b36a0fe8..b06117658c9e 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -186,7 +186,7 @@ UI::EventReturn UIScreen::OnCancel(UI::EventParams &e) { } PopupScreen::PopupScreen(std::string title, std::string button1, std::string button2) - : box_(0), title_(title), defaultButton_(NULL) { + : box_(0), defaultButton_(nullptr), title_(title) { I18NCategory *di = GetI18NCategory("Dialog"); if (!button1.empty()) button1_ = di->T(button1.c_str()); @@ -372,25 +372,25 @@ void PopupMultiChoice::Draw(UIContext &dc) { } PopupSliderChoice::PopupSliderChoice(int *value, int minValue, int maxValue, const std::string &text, ScreenManager *screenManager, const std::string &units, LayoutParams *layoutParams) - : Choice(text, "", false, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), step_(1), screenManager_(screenManager), units_(units) { + : Choice(text, "", false, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), step_(1), units_(units), screenManager_(screenManager) { fmt_ = "%i"; OnClick.Handle(this, &PopupSliderChoice::HandleClick); } PopupSliderChoice::PopupSliderChoice(int *value, int minValue, int maxValue, const std::string &text, int step, ScreenManager *screenManager, const std::string &units, LayoutParams *layoutParams) - : Choice(text, "", false, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), step_(step), screenManager_(screenManager), units_(units) { + : Choice(text, "", false, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), step_(step), units_(units), screenManager_(screenManager) { fmt_ = "%i"; OnClick.Handle(this, &PopupSliderChoice::HandleClick); } PopupSliderChoiceFloat::PopupSliderChoiceFloat(float *value, float minValue, float maxValue, const std::string &text, ScreenManager *screenManager, const std::string &units, LayoutParams *layoutParams) - : Choice(text, "", false, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), step_(1.0f), screenManager_(screenManager), units_(units) { + : Choice(text, "", false, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), step_(1.0f), units_(units), screenManager_(screenManager) { fmt_ = "%2.2f"; OnClick.Handle(this, &PopupSliderChoiceFloat::HandleClick); } PopupSliderChoiceFloat::PopupSliderChoiceFloat(float *value, float minValue, float maxValue, const std::string &text, float step, ScreenManager *screenManager, const std::string &units, LayoutParams *layoutParams) - : Choice(text, "", false, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), step_(step), screenManager_(screenManager), units_(units) { + : Choice(text, "", false, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), step_(step), units_(units), screenManager_(screenManager) { fmt_ = "%2.2f"; OnClick.Handle(this, &PopupSliderChoiceFloat::HandleClick); }