diff --git a/CMakeLists.txt b/CMakeLists.txt index 91f08e233b..25505d597c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1639,6 +1639,10 @@ set(GPU_SOURCES GPU/Common/TextureScalerCommon.h GPU/Common/PostShader.cpp GPU/Common/PostShader.h + GPU/Common/TextureReplacer.cpp + GPU/Common/TextureReplacer.h + GPU/Common/ReplacedTexture.cpp + GPU/Common/ReplacedTexture.h GPU/Debugger/Breakpoints.cpp GPU/Debugger/Breakpoints.h GPU/Debugger/Debugger.cpp @@ -2047,8 +2051,6 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Screenshot.h Core/System.cpp Core/System.h - Core/TextureReplacer.cpp - Core/TextureReplacer.h Core/ThreadPools.cpp Core/ThreadPools.h Core/Util/AudioFormat.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 0ad2df793f..0fad6b30a7 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -591,7 +591,6 @@ - @@ -1157,7 +1156,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 1fe06c2b6e..4b2b04de05 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -652,9 +652,6 @@ FileLoaders - - Core - MIPS\IR @@ -1737,9 +1734,6 @@ FileLoaders - - Core - MIPS\IR diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp new file mode 100644 index 0000000000..40b81c8a9b --- /dev/null +++ b/GPU/Common/ReplacedTexture.cpp @@ -0,0 +1,295 @@ +// 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 "ppsspp_config.h" + +#include + +#include "GPU/Common/ReplacedTexture.h" +#include "GPU/Common/TextureReplacer.h" + +#include "Common/Data/Format/IniFile.h" +#include "Common/Data/Format/ZIMLoad.h" +#include "Common/Data/Format/PNGLoad.h" +#include "Common/Thread/ParallelLoop.h" +#include "Common/Thread/Waitable.h" +#include "Common/Thread/ThreadManager.h" +#include "Common/Log.h" +#include "Common/TimeUtil.h" + +class ReplacedTextureTask : public Task { +public: + ReplacedTextureTask(VFSBackend *vfs, ReplacedTexture &tex, LimitedWaitable *w) : vfs_(vfs), tex_(tex), waitable_(w) {} + + TaskType Type() const override { + return TaskType::IO_BLOCKING; + } + + TaskPriority Priority() const override { + return TaskPriority::NORMAL; + } + + void Run() override { + tex_.Prepare(vfs_); + waitable_->Notify(); + } + +private: + VFSBackend *vfs_; + ReplacedTexture &tex_; + LimitedWaitable *waitable_; +}; + +bool ReplacedTexture::IsReady(double budget) { + _assert_(vfs_ != nullptr); + lastUsed_ = time_now_d(); + if (threadWaitable_ && !threadWaitable_->WaitFor(budget)) { + return false; + } + + // Loaded already, or not yet on a thread? + if (initDone_ && levelData_ && !levelData_->data.empty()) { + // TODO: lock? + levelData_->lastUsed = lastUsed_; + return true; + } + + // Let's not even start a new texture if we're already behind. + if (budget < 0.0) + return false; + if (!prepareDone_) + return false; + + if (threadWaitable_) + delete threadWaitable_; + threadWaitable_ = new LimitedWaitable(); + g_threadManager.EnqueueTask(new ReplacedTextureTask(vfs_, *this, threadWaitable_)); + if (threadWaitable_->WaitFor(budget)) { + // If we finished all the levels, we're done. + return initDone_ && levelData_ != nullptr && !levelData_->data.empty(); + } + + // Still pending on thread. + return false; +} + +void ReplacedTexture::Prepare(VFSBackend *vfs) { + std::unique_lock lock(mutex_); + this->vfs_ = vfs; + if (cancelPrepare_) { + initDone_ = true; + return; + } + + for (int i = 0; i < (int)levels_.size(); ++i) { + if (cancelPrepare_) + break; + PrepareData(i); + } + + initDone_ = true; + if (!cancelPrepare_ && threadWaitable_) + threadWaitable_->Notify(); +} + +void ReplacedTexture::PrepareData(int level) { + _assert_msg_((size_t)level < levels_.size(), "Invalid miplevel"); + _assert_msg_(levelData_ != nullptr, "Level cache not set"); + + // We must lock around access to levelData_ in case two textures try to load it at once. + std::lock_guard guard(levelData_->lock); + + const ReplacedTextureLevel &info = levels_[level]; + + if (levelData_->data.size() <= level) { + levelData_->data.resize(level + 1); + } + + std::vector &out = levelData_->data[level]; + + // Already populated from cache. + if (!out.empty()) + return; + + ReplacedImageType imageType; + + size_t fileSize; + VFSOpenFile *openFile = vfs_->OpenFileForRead(info.fileRef, &fileSize); + + std::string magic; + imageType = Identify(vfs_, openFile, &magic); + + auto cleanup = [&] { + vfs_->CloseFile(openFile); + }; + + if (imageType == ReplacedImageType::ZIM) { + std::unique_ptr zim(new uint8_t[fileSize]); + if (!zim) { + ERROR_LOG(G3D, "Failed to allocate memory for texture replacement"); + cleanup(); + return; + } + + if (vfs_->Read(openFile, &zim[0], fileSize) != fileSize) { + ERROR_LOG(G3D, "Could not load texture replacement: %s - failed to read ZIM", info.file.c_str()); + cleanup(); + return; + } + + int w, h, f; + uint8_t *image; + if (LoadZIMPtr(&zim[0], fileSize, &w, &h, &f, &image)) { + if (w > info.w || h > info.h) { + ERROR_LOG(G3D, "Texture replacement changed since header read: %s", info.file.c_str()); + cleanup(); + return; + } + + out.resize(info.w * info.h * 4); + if (w == info.w) { + memcpy(&out[0], image, info.w * 4 * info.h); + } else { + for (int y = 0; y < h; ++y) { + memcpy(&out[info.w * 4 * y], image + w * 4 * y, w * 4); + } + } + free(image); + } + + CheckAlphaResult res = CheckAlpha32Rect((u32 *)&out[0], info.w, w, h, 0xFF000000); + if (res == CHECKALPHA_ANY || level == 0) { + alphaStatus_ = ReplacedTextureAlpha(res); + } + } else if (imageType == ReplacedImageType::PNG) { + png_image png = {}; + png.version = PNG_IMAGE_VERSION; + + std::string pngdata; + pngdata.resize(fileSize); + pngdata.resize(vfs_->Read(openFile, &pngdata[0], fileSize)); + if (!png_image_begin_read_from_memory(&png, &pngdata[0], pngdata.size())) { + ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s (zip)", info.file.c_str(), png.message); + cleanup(); + return; + } + if (png.width > (uint32_t)info.w || png.height > (uint32_t)info.h) { + ERROR_LOG(G3D, "Texture replacement changed since header read: %s", info.file.c_str()); + cleanup(); + return; + } + + bool checkedAlpha = false; + if ((png.format & PNG_FORMAT_FLAG_ALPHA) == 0) { + // Well, we know for sure it doesn't have alpha. + if (level == 0) { + alphaStatus_ = ReplacedTextureAlpha::FULL; + } + checkedAlpha = true; + } + png.format = PNG_FORMAT_RGBA; + + out.resize(info.w * info.h * 4); + if (!png_image_finish_read(&png, nullptr, &out[0], info.w * 4, nullptr)) { + ERROR_LOG(G3D, "Could not load texture replacement: %s - %s", info.file.c_str(), png.message); + cleanup(); + out.resize(0); + return; + } + png_image_free(&png); + + if (!checkedAlpha) { + // This will only check the hashed bits. + CheckAlphaResult res = CheckAlpha32Rect((u32 *)&out[0], info.w, png.width, png.height, 0xFF000000); + if (res == CHECKALPHA_ANY || level == 0) { + alphaStatus_ = ReplacedTextureAlpha(res); + } + } + } + + cleanup(); +} + +void ReplacedTexture::PurgeIfOlder(double t) { + if (threadWaitable_ && !threadWaitable_->WaitFor(0.0)) + return; + if (lastUsed_ >= t) + return; + + if (levelData_->lastUsed < t) { + // We have to lock since multiple textures might reference this same data. + std::lock_guard guard(levelData_->lock); + levelData_->data.clear(); + // This means we have to reload. If we never purge any, there's no need. + initDone_ = false; + } +} + +ReplacedTexture::~ReplacedTexture() { + if (threadWaitable_) { + cancelPrepare_ = true; + + std::unique_lock lock(mutex_); + threadWaitable_->WaitAndRelease(); + threadWaitable_ = nullptr; + } + + for (auto &level : levels_) { + vfs_->ReleaseFile(level.fileRef); + level.fileRef = nullptr; + } +} + +bool ReplacedTexture::CopyLevelTo(int level, void *out, int rowPitch) { + _assert_msg_((size_t)level < levels_.size(), "Invalid miplevel"); + _assert_msg_(out != nullptr && rowPitch > 0, "Invalid out/pitch"); + + if (!initDone_) { + WARN_LOG(G3D, "Init not done yet"); + return false; + } + + // We probably could avoid this lock, but better to play it safe. + std::lock_guard guard(levelData_->lock); + + const ReplacedTextureLevel &info = levels_[level]; + const std::vector &data = levelData_->data[level]; + + if (data.empty()) { + WARN_LOG(G3D, "Level %d is empty", level); + return false; + } + + if (rowPitch < info.w * 4) { + ERROR_LOG(G3D, "Replacement rowPitch=%d, but w=%d (level=%d)", rowPitch, info.w * 4, level); + return false; + } + _assert_msg_(data.size() == info.w * info.h * 4, "Data has wrong size"); + + if (rowPitch == info.w * 4) { + ParallelMemcpy(&g_threadManager, out, &data[0], info.w * 4 * info.h); + } else { + const int MIN_LINES_PER_THREAD = 4; + ParallelRangeLoop(&g_threadManager, [&](int l, int h) { + for (int y = l; y < h; ++y) { + memcpy((uint8_t *)out + rowPitch * y, &data[0] + info.w * 4 * y, info.w * 4); + } + }, 0, info.h, MIN_LINES_PER_THREAD); + } + + return true; +} diff --git a/GPU/Common/ReplacedTexture.h b/GPU/Common/ReplacedTexture.h new file mode 100644 index 0000000000..4e00d8df28 --- /dev/null +++ b/GPU/Common/ReplacedTexture.h @@ -0,0 +1,132 @@ +// 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 + +#include "Common/File/VFS/VFS.h" +#include "Common/GPU/thin3d.h" + +struct ReplacedLevelsCache; +class TextureReplacer; +class LimitedWaitable; + +// These must match the constants in TextureCacheCommon. +enum class ReplacedTextureAlpha { + UNKNOWN = 0x04, + FULL = 0x00, +}; + +// For forward compatibility, we specify the hash. +enum class ReplacedTextureHash { + QUICK, + XXH32, + XXH64, +}; + +enum class ReplacedImageType { + PNG, + ZIM, + INVALID, +}; + +// Metadata about a given texture level. +struct ReplacedTextureLevel { + int w = 0; + int h = 0; + Path file; + + // To be able to reload, we need to be able to reopen, unfortunate we can't use zip_file_t. + // TODO: This really belongs on the level in the cache, not in the individual ReplacedTextureLevel objects. + VFSFileReference *fileRef = nullptr; +}; + +ReplacedImageType Identify(VFSBackend *vfs, VFSOpenFile *openFile, std::string *outMagic); + +struct ReplacedTexture { + ~ReplacedTexture(); + + inline bool Valid() const { + if (!initDone_) + return false; + return !levels_.empty(); + } + + inline bool IsInvalid() const { + if (!initDone_) + return false; + return levels_.empty(); + } + + bool GetSize(int level, int &w, int &h) const { + if (!initDone_) + return false; + if ((size_t)level < levels_.size()) { + w = levels_[level].w; + h = levels_[level].h; + return true; + } + return false; + } + + int NumLevels() const { + if (!initDone_) + return 0; + return (int)levels_.size(); + } + + Draw::DataFormat Format() const { + if (initDone_) { + return fmt; + } else { + // Shouldn't get here. + return Draw::DataFormat::UNDEFINED; + } + } + + u8 AlphaStatus() const { + return (u8)alphaStatus_; + } + + bool IsReady(double budget); + bool CopyLevelTo(int level, void *out, int rowPitch); + +protected: + void Prepare(VFSBackend *vfs); + void PrepareData(int level); + void PurgeIfOlder(double t); + + std::vector levels_; + ReplacedLevelsCache *levelData_; + + ReplacedTextureAlpha alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; + double lastUsed_ = 0.0; + LimitedWaitable *threadWaitable_ = nullptr; + std::mutex mutex_; + Draw::DataFormat fmt = Draw::DataFormat::UNDEFINED; // NOTE: Right now, the only supported format is Draw::DataFormat::R8G8B8A8_UNORM. + + bool cancelPrepare_ = false; + bool initDone_ = false; + bool prepareDone_ = false; + + VFSBackend *vfs_ = nullptr; + + friend class TextureReplacer; + friend class ReplacedTextureTask; +}; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index da42b07de4..7db6507b13 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -23,13 +23,13 @@ #include "Common/CommonTypes.h" #include "Common/MemoryUtil.h" -#include "Core/TextureReplacer.h" #include "Core/System.h" #include "GPU/GPU.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Common/TextureDecoder.h" #include "GPU/Common/TextureScalerCommon.h" #include "GPU/Common/TextureShaderCommon.h" +#include "GPU/Common/TextureReplacer.h" class Draw2D; diff --git a/Core/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp similarity index 80% rename from Core/TextureReplacer.cpp rename to GPU/Common/TextureReplacer.cpp index 2ac2e23133..296acc0031 100644 --- a/Core/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -18,7 +18,6 @@ #include "ppsspp_config.h" #include -#include #include #include #include @@ -44,9 +43,9 @@ #include "Core/Config.h" #include "Core/Host.h" #include "Core/System.h" -#include "Core/TextureReplacer.h" #include "Core/ThreadPools.h" #include "Core/ELF/ParamSFO.h" +#include "GPU/Common/TextureReplacer.h" #include "GPU/Common/TextureDecoder.h" static const std::string INI_FILENAME = "textures.ini"; @@ -56,12 +55,6 @@ static const int VERSION = 1; static const int MAX_MIP_LEVELS = 12; // 12 should be plenty, 8 is the max mip levels supported by the PSP. static const double MAX_CACHE_SIZE = 4.0; -enum class ReplacedImageType { - PNG, - ZIM, - INVALID, -}; - static inline ReplacedImageType IdentifyMagic(const uint8_t magic[4]) { if (strncmp((const char *)magic, "ZIMG", 4) == 0) return ReplacedImageType::ZIM; @@ -70,7 +63,7 @@ static inline ReplacedImageType IdentifyMagic(const uint8_t magic[4]) { return ReplacedImageType::INVALID; } -static ReplacedImageType Identify(VFSBackend *vfs, VFSOpenFile *openFile, std::string *outMagic) { +ReplacedImageType Identify(VFSBackend *vfs, VFSOpenFile *openFile, std::string *outMagic) { uint8_t magic[4]; if (vfs->Read(openFile, magic, 4) != 4) { *outMagic = "FAIL"; @@ -955,269 +948,6 @@ float TextureReplacer::LookupReduceHashRange(int& w, int& h) { } } -class ReplacedTextureTask : public Task { -public: - ReplacedTextureTask(VFSBackend *vfs, ReplacedTexture &tex, LimitedWaitable *w) : vfs_(vfs), tex_(tex), waitable_(w) {} - - TaskType Type() const override { - return TaskType::IO_BLOCKING; - } - - TaskPriority Priority() const override { - return TaskPriority::NORMAL; - } - - void Run() override { - tex_.Prepare(vfs_); - waitable_->Notify(); - } - -private: - VFSBackend *vfs_; - ReplacedTexture &tex_; - LimitedWaitable *waitable_; -}; - -bool ReplacedTexture::IsReady(double budget) { - _assert_(vfs_ != nullptr); - lastUsed_ = time_now_d(); - if (threadWaitable_ && !threadWaitable_->WaitFor(budget)) { - return false; - } - - // Loaded already, or not yet on a thread? - if (initDone_ && levelData_ && !levelData_->data.empty()) { - // TODO: lock? - levelData_->lastUsed = lastUsed_; - return true; - } - - // Let's not even start a new texture if we're already behind. - if (budget < 0.0) - return false; - if (!prepareDone_) - return false; - - if (threadWaitable_) - delete threadWaitable_; - threadWaitable_ = new LimitedWaitable(); - g_threadManager.EnqueueTask(new ReplacedTextureTask(vfs_, *this, threadWaitable_)); - if (threadWaitable_->WaitFor(budget)) { - // If we finished all the levels, we're done. - return initDone_ && levelData_ != nullptr && !levelData_->data.empty(); - } - - // Still pending on thread. - return false; -} - -void ReplacedTexture::Prepare(VFSBackend *vfs) { - std::unique_lock lock(mutex_); - this->vfs_ = vfs; - if (cancelPrepare_) { - initDone_ = true; - return; - } - - for (int i = 0; i < (int)levels_.size(); ++i) { - if (cancelPrepare_) - break; - PrepareData(i); - } - - initDone_ = true; - if (!cancelPrepare_ && threadWaitable_) - threadWaitable_->Notify(); -} - -void ReplacedTexture::PrepareData(int level) { - _assert_msg_((size_t)level < levels_.size(), "Invalid miplevel"); - _assert_msg_(levelData_ != nullptr, "Level cache not set"); - - // We must lock around access to levelData_ in case two textures try to load it at once. - std::lock_guard guard(levelData_->lock); - - const ReplacedTextureLevel &info = levels_[level]; - - if (levelData_->data.size() <= level) { - levelData_->data.resize(level + 1); - } - - std::vector &out = levelData_->data[level]; - - // Already populated from cache. - if (!out.empty()) - return; - - ReplacedImageType imageType; - - size_t fileSize; - VFSOpenFile *openFile = vfs_->OpenFileForRead(info.fileRef, &fileSize); - - std::string magic; - imageType = Identify(vfs_, openFile, &magic); - - auto cleanup = [&] { - vfs_->CloseFile(openFile); - }; - - if (imageType == ReplacedImageType::ZIM) { - std::unique_ptr zim(new uint8_t[fileSize]); - if (!zim) { - ERROR_LOG(G3D, "Failed to allocate memory for texture replacement"); - cleanup(); - return; - } - - if (vfs_->Read(openFile, &zim[0], fileSize) != fileSize) { - ERROR_LOG(G3D, "Could not load texture replacement: %s - failed to read ZIM", info.file.c_str()); - cleanup(); - return; - } - - int w, h, f; - uint8_t *image; - if (LoadZIMPtr(&zim[0], fileSize, &w, &h, &f, &image)) { - if (w > info.w || h > info.h) { - ERROR_LOG(G3D, "Texture replacement changed since header read: %s", info.file.c_str()); - cleanup(); - return; - } - - out.resize(info.w * info.h * 4); - if (w == info.w) { - memcpy(&out[0], image, info.w * 4 * info.h); - } else { - for (int y = 0; y < h; ++y) { - memcpy(&out[info.w * 4 * y], image + w * 4 * y, w * 4); - } - } - free(image); - } - - CheckAlphaResult res = CheckAlpha32Rect((u32 *)&out[0], info.w, w, h, 0xFF000000); - if (res == CHECKALPHA_ANY || level == 0) { - alphaStatus_ = ReplacedTextureAlpha(res); - } - } else if (imageType == ReplacedImageType::PNG) { - png_image png = {}; - png.version = PNG_IMAGE_VERSION; - - std::string pngdata; - pngdata.resize(fileSize); - pngdata.resize(vfs_->Read(openFile, &pngdata[0], fileSize)); - if (!png_image_begin_read_from_memory(&png, &pngdata[0], pngdata.size())) { - ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s (zip)", info.file.c_str(), png.message); - cleanup(); - return; - } - if (png.width > (uint32_t)info.w || png.height > (uint32_t)info.h) { - ERROR_LOG(G3D, "Texture replacement changed since header read: %s", info.file.c_str()); - cleanup(); - return; - } - - bool checkedAlpha = false; - if ((png.format & PNG_FORMAT_FLAG_ALPHA) == 0) { - // Well, we know for sure it doesn't have alpha. - if (level == 0) { - alphaStatus_ = ReplacedTextureAlpha::FULL; - } - checkedAlpha = true; - } - png.format = PNG_FORMAT_RGBA; - - out.resize(info.w * info.h * 4); - if (!png_image_finish_read(&png, nullptr, &out[0], info.w * 4, nullptr)) { - ERROR_LOG(G3D, "Could not load texture replacement: %s - %s", info.file.c_str(), png.message); - cleanup(); - out.resize(0); - return; - } - png_image_free(&png); - - if (!checkedAlpha) { - // This will only check the hashed bits. - CheckAlphaResult res = CheckAlpha32Rect((u32 *)&out[0], info.w, png.width, png.height, 0xFF000000); - if (res == CHECKALPHA_ANY || level == 0) { - alphaStatus_ = ReplacedTextureAlpha(res); - } - } - } - - cleanup(); -} - -void ReplacedTexture::PurgeIfOlder(double t) { - if (threadWaitable_ && !threadWaitable_->WaitFor(0.0)) - return; - if (lastUsed_ >= t) - return; - - if (levelData_->lastUsed < t) { - // We have to lock since multiple textures might reference this same data. - std::lock_guard guard(levelData_->lock); - levelData_->data.clear(); - // This means we have to reload. If we never purge any, there's no need. - initDone_ = false; - } -} - -ReplacedTexture::~ReplacedTexture() { - if (threadWaitable_) { - cancelPrepare_ = true; - - std::unique_lock lock(mutex_); - threadWaitable_->WaitAndRelease(); - threadWaitable_ = nullptr; - } - - for (auto &level : levels_) { - vfs_->ReleaseFile(level.fileRef); - level.fileRef = nullptr; - } -} - -bool ReplacedTexture::CopyLevelTo(int level, void *out, int rowPitch) { - _assert_msg_((size_t)level < levels_.size(), "Invalid miplevel"); - _assert_msg_(out != nullptr && rowPitch > 0, "Invalid out/pitch"); - - if (!initDone_) { - WARN_LOG(G3D, "Init not done yet"); - return false; - } - - // We probably could avoid this lock, but better to play it safe. - std::lock_guard guard(levelData_->lock); - - const ReplacedTextureLevel &info = levels_[level]; - const std::vector &data = levelData_->data[level]; - - if (data.empty()) { - WARN_LOG(G3D, "Level %d is empty", level); - return false; - } - - if (rowPitch < info.w * 4) { - ERROR_LOG(G3D, "Replacement rowPitch=%d, but w=%d (level=%d)", rowPitch, info.w * 4, level); - return false; - } - _assert_msg_(data.size() == info.w * info.h * 4, "Data has wrong size"); - - if (rowPitch == info.w * 4) { - ParallelMemcpy(&g_threadManager, out, &data[0], info.w * 4 * info.h); - } else { - const int MIN_LINES_PER_THREAD = 4; - ParallelRangeLoop(&g_threadManager, [&](int l, int h) { - for (int y = l; y < h; ++y) { - memcpy((uint8_t *)out + rowPitch * y, &data[0] + info.w * 4 * y, info.w * 4); - } - }, 0, info.h, MIN_LINES_PER_THREAD); - } - - return true; -} - bool TextureReplacer::IniExists(const std::string &gameID) { if (gameID.empty()) return false; diff --git a/Core/TextureReplacer.h b/GPU/Common/TextureReplacer.h similarity index 71% rename from Core/TextureReplacer.h rename to GPU/Common/TextureReplacer.h index a2faa7a763..1efc9d7a54 100644 --- a/Core/TextureReplacer.h +++ b/GPU/Common/TextureReplacer.h @@ -31,6 +31,7 @@ #include "Common/GPU/DataFormat.h" #include "GPU/Common/TextureDecoder.h" +#include "GPU/Common/ReplacedTexture.h" #include "GPU/ge_constants.h" class IniFile; @@ -40,30 +41,6 @@ class ReplacedTextureTask; class LimitedWaitable; class VFSBackend; -// These must match the constants in TextureCacheCommon. -enum class ReplacedTextureAlpha { - UNKNOWN = 0x04, - FULL = 0x00, -}; - -// For forward compatibility, we specify the hash. -enum class ReplacedTextureHash { - QUICK, - XXH32, - XXH64, -}; - -// Metadata about a given texture level. -struct ReplacedTextureLevel { - int w = 0; - int h = 0; - Path file; - - // To be able to reload, we need to be able to reopen, unfortunate we can't use zip_file_t. - // TODO: This really belongs on the level in the cache, not in the individual ReplacedTextureLevel objects. - VFSFileReference *fileRef = nullptr; -}; - struct SavedTextureCacheData { int levelW[8]{}; int levelH[8]{}; @@ -104,78 +81,6 @@ namespace std { }; } -struct ReplacedTexture { - ~ReplacedTexture(); - - inline bool Valid() const { - if (!initDone_) - return false; - return !levels_.empty(); - } - - inline bool IsInvalid() const { - if (!initDone_) - return false; - return levels_.empty(); - } - - bool GetSize(int level, int &w, int &h) const { - if (!initDone_) - return false; - if ((size_t)level < levels_.size()) { - w = levels_[level].w; - h = levels_[level].h; - return true; - } - return false; - } - - int NumLevels() const { - if (!initDone_) - return 0; - return (int)levels_.size(); - } - - Draw::DataFormat Format() const { - if (initDone_) { - return fmt; - } else { - // Shouldn't get here. - return Draw::DataFormat::UNDEFINED; - } - } - - u8 AlphaStatus() const { - return (u8)alphaStatus_; - } - - bool IsReady(double budget); - bool CopyLevelTo(int level, void *out, int rowPitch); - -protected: - void Prepare(VFSBackend *vfs); - void PrepareData(int level); - void PurgeIfOlder(double t); - - std::vector levels_; - ReplacedLevelsCache *levelData_; - - ReplacedTextureAlpha alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; - double lastUsed_ = 0.0; - LimitedWaitable *threadWaitable_ = nullptr; - std::mutex mutex_; - Draw::DataFormat fmt = Draw::DataFormat::UNDEFINED; // NOTE: Right now, the only supported format is Draw::DataFormat::R8G8B8A8_UNORM. - - bool cancelPrepare_ = false; - bool initDone_ = false; - bool prepareDone_ = false; - - VFSBackend *vfs_ = nullptr; - - friend class TextureReplacer; - friend class ReplacedTextureTask; -}; - struct ReplacedTextureDecodeInfo { u64 cachekey; u32 hash; diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 606e169859..3b6f07c66f 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -164,7 +164,7 @@ Level3 - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib;../ext/libpng17 _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) StreamingSIMDExtensions2 Precise @@ -182,7 +182,7 @@ Level3 - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib;../ext/libpng17 NotSet Precise false @@ -202,7 +202,7 @@ Level3 - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib;../ext/libpng17 NotSet Precise false @@ -221,7 +221,7 @@ Level3 - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib;../ext/libpng17 NotSet Precise false @@ -244,7 +244,7 @@ MaxSpeed true true - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib;../ext/libpng17 false StreamingSIMDExtensions2 Precise @@ -267,7 +267,7 @@ MaxSpeed true true - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib;../ext/libpng17 false NotSet Precise @@ -292,7 +292,7 @@ MaxSpeed true true - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib;../ext/libpng17 false NotSet Precise @@ -317,7 +317,7 @@ MaxSpeed true true - ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib + ..\dx9sdk\Include\DX11;../common;..;../ext;../ext/glew;../ext/snappy;../ext/glslang;../ext/zstd/lib;../ext/libpng17 false NotSet Precise @@ -338,6 +338,8 @@ + + @@ -456,6 +458,8 @@ + + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index b34942e06c..6b35f8f33a 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -267,6 +267,12 @@ Common + + Common + + + Common + @@ -530,6 +536,12 @@ Common + + Common + + + Common + diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index abf2e2034f..0ef3060135 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -66,10 +66,10 @@ #include "Core/Instance.h" #include "Core/System.h" #include "Core/Reporting.h" -#include "Core/TextureReplacer.h" #include "Core/WebServer.h" #include "Core/HLE/sceUsbCam.h" #include "Core/HLE/sceUsbMic.h" +#include "GPU/Common/TextureReplacer.h" #include "GPU/Common/PostShader.h" #include "android/jni/TestRunner.h" #include "GPU/GPUInterface.h" diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 65b384657b..6fe4ea1320 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -476,7 +476,6 @@ - @@ -739,7 +738,6 @@ - @@ -758,459 +756,573 @@ NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing - + + NotUsing diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 119c2d0936..7fead01f06 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -108,7 +108,6 @@ - MIPS\ARM @@ -1150,7 +1149,6 @@ - diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 0448b3e1bd..0548c117a6 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -90,7 +90,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -105,7 +105,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -120,7 +120,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -135,7 +135,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -150,7 +150,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -165,7 +165,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -180,7 +180,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -195,7 +195,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -210,7 +210,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) @@ -225,7 +225,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -240,7 +240,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -255,7 +255,7 @@ false false pch.h - ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) + ../..;../../ext/snappy;../../ext/native;../../Common;../../ext;../../ext/glslang;../../ext/zstd/lib;../../ext/libpng17;$(ProjectDir);$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;NOMINMAX;_UNICODE;UNICODE;%(PreprocessorDefinitions) @@ -265,6 +265,8 @@ + + @@ -329,6 +331,8 @@ + + diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters index dc746a6f6d..314574f863 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters @@ -62,6 +62,8 @@ + + @@ -125,5 +127,7 @@ + + \ No newline at end of file diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 3cc90b6dcf..57b695f3e5 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -389,6 +389,8 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/ShaderUniforms.cpp \ $(SRC)/GPU/Common/VertexShaderGenerator.cpp \ $(SRC)/GPU/Common/GeometryShaderGenerator.cpp \ + $(SRC)/GPU/Common/TextureReplacer.cpp \ + $(SRC)/GPU/Common/ReplacedTexture.cpp \ $(SRC)/GPU/Debugger/Breakpoints.cpp \ $(SRC)/GPU/Debugger/Debugger.cpp \ $(SRC)/GPU/Debugger/GECommandTable.cpp \ @@ -456,7 +458,6 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/SaveState.cpp \ $(SRC)/Core/Screenshot.cpp \ $(SRC)/Core/System.cpp \ - $(SRC)/Core/TextureReplacer.cpp \ $(SRC)/Core/TiltEventProcessor.cpp \ $(SRC)/Core/ThreadPools.cpp \ $(SRC)/Core/WebServer.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 2b3bcfed99..67149d94d4 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -386,6 +386,8 @@ SOURCES_CXX += \ $(GPUCOMMONDIR)/IndexGenerator.cpp \ $(GPUCOMMONDIR)/TextureDecoder.cpp \ $(GPUCOMMONDIR)/PostShader.cpp \ + $(GPUCOMMONDIR)/TextureReplacer.cpp \ + $(GPUCOMMONDIR)/ReplacedTexture.cpp \ $(COMMONDIR)/Data/Convert/ColorConv.cpp \ $(GPUDIR)/Debugger/Breakpoints.cpp \ $(GPUDIR)/Debugger/Debugger.cpp \ @@ -495,7 +497,6 @@ SOURCES_CXX += \ $(COREDIR)/AVIDump.cpp \ $(COREDIR)/Config.cpp \ $(COREDIR)/ControlMapper.cpp \ - $(COREDIR)/TextureReplacer.cpp \ $(COREDIR)/Core.cpp \ $(COREDIR)/WaveFile.cpp \ $(COREDIR)/KeyMap.cpp \