mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #15025 from unknownbrackets/texreplace-pop
Allow delayed loading of texture replacements
This commit is contained in:
commit
edc4e69c3d
10 changed files with 271 additions and 37 deletions
|
@ -887,6 +887,7 @@ static ConfigSetting graphicsSettings[] = {
|
|||
ReportedConfigSetting("ReplaceTextures", &g_Config.bReplaceTextures, true, true, true),
|
||||
ReportedConfigSetting("SaveNewTextures", &g_Config.bSaveNewTextures, false, true, true),
|
||||
ConfigSetting("IgnoreTextureFilenames", &g_Config.bIgnoreTextureFilenames, false, true, true),
|
||||
ConfigSetting("ReplaceTexturesAllowLate", &g_Config.bReplaceTexturesAllowLate, true, true, true),
|
||||
|
||||
ReportedConfigSetting("TexScalingLevel", &g_Config.iTexScalingLevel, 1, true, true),
|
||||
ReportedConfigSetting("TexScalingType", &g_Config.iTexScalingType, 0, true, true),
|
||||
|
|
|
@ -197,6 +197,7 @@ public:
|
|||
bool bReplaceTextures;
|
||||
bool bSaveNewTextures;
|
||||
bool bIgnoreTextureFilenames;
|
||||
bool bReplaceTexturesAllowLate;
|
||||
int iTexScalingLevel; // 0 = auto, 1 = off, 2 = 2x, ..., 5 = 5x
|
||||
int iTexScalingType; // 0 = xBRZ, 1 = Hybrid
|
||||
bool bTexDeposterize;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "ppsspp_config.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <png.h>
|
||||
|
@ -31,6 +32,7 @@
|
|||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/Thread/ParallelLoop.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/System.h"
|
||||
|
@ -621,6 +623,15 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
|
|||
savedCache_[replacementKey] = saved;
|
||||
}
|
||||
|
||||
void TextureReplacer::Decimate(bool forcePressure) {
|
||||
// Allow replacements to be cached for a long time, although they're large.
|
||||
const double age = forcePressure ? 90.0 : 1800.0;
|
||||
const double threshold = time_now_d() - age;
|
||||
for (auto &item : cache_) {
|
||||
item.second.PurgeIfOlder(threshold);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
static typename std::unordered_map<Key, Value>::const_iterator LookupWildcard(const std::unordered_map<Key, Value> &map, Key &key, u64 cachekey, u32 hash, bool ignoreAddress) {
|
||||
auto alias = map.find(key);
|
||||
|
@ -733,15 +744,114 @@ float TextureReplacer::LookupReduceHashRange(int& w, int& h) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ReplacedTexture::Load(int level, void *out, int rowPitch) {
|
||||
class LimitedWaitable : public Waitable {
|
||||
public:
|
||||
LimitedWaitable() {
|
||||
triggered_ = false;
|
||||
}
|
||||
|
||||
void Wait() override {
|
||||
if (!triggered_) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cond_.wait(lock, [&] { return !triggered_; });
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitFor(double budget) {
|
||||
uint32_t us = budget > 0 ? (uint32_t)(budget * 1000000.0) : 0;
|
||||
if (!triggered_) {
|
||||
if (us == 0)
|
||||
return false;
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cond_.wait_for(lock, std::chrono::microseconds(us), [&] { return !triggered_; });
|
||||
}
|
||||
return triggered_;
|
||||
}
|
||||
|
||||
void Notify() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
triggered_ = true;
|
||||
cond_.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
std::condition_variable cond_;
|
||||
std::mutex mutex_;
|
||||
std::atomic<bool> triggered_;
|
||||
};
|
||||
|
||||
class ReplacedTextureTask : public Task {
|
||||
public:
|
||||
ReplacedTextureTask(ReplacedTexture &tex, LimitedWaitable *w) : tex_(tex), waitable_(w) {
|
||||
}
|
||||
|
||||
void Run() override {
|
||||
tex_.Prepare();
|
||||
waitable_->Notify();
|
||||
}
|
||||
|
||||
private:
|
||||
ReplacedTexture &tex_;
|
||||
LimitedWaitable *waitable_;
|
||||
};
|
||||
|
||||
bool ReplacedTexture::IsReady(double budget) {
|
||||
lastUsed_ = time_now_d();
|
||||
if (threadWaitable_) {
|
||||
if (!threadWaitable_->WaitFor(budget)) {
|
||||
return false;
|
||||
} else {
|
||||
threadWaitable_->WaitAndRelease();
|
||||
threadWaitable_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Loaded already, or not yet on a thread?
|
||||
if (!levelData_.empty())
|
||||
return true;
|
||||
// Let's not even start a new texture if we're already behind.
|
||||
if (budget < 0.0)
|
||||
return false;
|
||||
|
||||
if (g_Config.bReplaceTexturesAllowLate) {
|
||||
threadWaitable_ = new LimitedWaitable();
|
||||
g_threadManager.EnqueueTask(new ReplacedTextureTask(*this, threadWaitable_), TaskType::IO_BLOCKING);
|
||||
|
||||
if (threadWaitable_->WaitFor(budget)) {
|
||||
threadWaitable_->WaitAndRelease();
|
||||
threadWaitable_ = nullptr;
|
||||
|
||||
// If we finished all the levels, we're done.
|
||||
return !levelData_.empty();
|
||||
}
|
||||
} else {
|
||||
Prepare();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Still pending on thread.
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReplacedTexture::Prepare() {
|
||||
levelData_.resize(MaxLevel() + 1);
|
||||
for (int i = 0; i <= MaxLevel(); ++i) {
|
||||
if (cancelPrepare_)
|
||||
break;
|
||||
PrepareData(i);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplacedTexture::PrepareData(int level) {
|
||||
_assert_msg_((size_t)level < levels_.size(), "Invalid miplevel");
|
||||
_assert_msg_(out != nullptr && rowPitch > 0, "Invalid out/pitch");
|
||||
|
||||
const ReplacedTextureLevel &info = levels_[level];
|
||||
std::vector<uint8_t> &out = levelData_[level];
|
||||
|
||||
FILE *fp = File::OpenCFile(info.file, "rb");
|
||||
if (!fp) {
|
||||
return false;
|
||||
// Leaving the data sized at zero means failure.
|
||||
return;
|
||||
}
|
||||
|
||||
auto imageType = Identify(fp);
|
||||
|
@ -751,29 +861,36 @@ bool ReplacedTexture::Load(int level, void *out, int rowPitch) {
|
|||
if (!zim) {
|
||||
ERROR_LOG(G3D, "Failed to allocate memory for texture replacement");
|
||||
fclose(fp);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fread(&zim[0], 1, zimSize, fp) != zimSize) {
|
||||
ERROR_LOG(G3D, "Could not load texture replacement: %s - failed to read ZIM", info.file.c_str());
|
||||
fclose(fp);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
int w, h, f;
|
||||
uint8_t *image;
|
||||
const int MIN_LINES_PER_THREAD = 4;
|
||||
if (LoadZIMPtr(&zim[0], zimSize, &w, &h, &f, &image)) {
|
||||
ParallelRangeLoop(&g_threadManager, [&](int l, int h) {
|
||||
for (int y = l; y < h; ++y) {
|
||||
memcpy((uint8_t *)out + rowPitch * y, image + w * 4 * y, w * 4);
|
||||
if (w > info.w || h > info.h) {
|
||||
ERROR_LOG(G3D, "Texture replacement changed since header read: %s", info.file.c_str());
|
||||
fclose(fp);
|
||||
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);
|
||||
}
|
||||
}, 0, h, MIN_LINES_PER_THREAD);
|
||||
}
|
||||
free(image);
|
||||
}
|
||||
|
||||
// This will only check the hashed bits.
|
||||
CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)out, rowPitch / sizeof(u32), w, h);
|
||||
CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)&out[0], info.w, w, h);
|
||||
if (res == CHECKALPHA_ANY || level == 0) {
|
||||
alphaStatus_ = ReplacedTextureAlpha(res);
|
||||
}
|
||||
|
@ -784,7 +901,12 @@ bool ReplacedTexture::Load(int level, void *out, int rowPitch) {
|
|||
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);
|
||||
fclose(fp);
|
||||
return false;
|
||||
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());
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
bool checkedAlpha = false;
|
||||
|
@ -797,16 +919,18 @@ bool ReplacedTexture::Load(int level, void *out, int rowPitch) {
|
|||
}
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
|
||||
if (!png_image_finish_read(&png, nullptr, out, rowPitch, nullptr)) {
|
||||
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);
|
||||
fclose(fp);
|
||||
return false;
|
||||
out.resize(0);
|
||||
return;
|
||||
}
|
||||
png_image_free(&png);
|
||||
|
||||
if (!checkedAlpha) {
|
||||
// This will only check the hashed bits.
|
||||
CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)out, rowPitch / sizeof(u32), png.width, png.height);
|
||||
CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)&out[0], info.w, png.width, png.height);
|
||||
if (res == CHECKALPHA_ANY || level == 0) {
|
||||
alphaStatus_ = ReplacedTextureAlpha(res);
|
||||
}
|
||||
|
@ -814,6 +938,47 @@ bool ReplacedTexture::Load(int level, void *out, int rowPitch) {
|
|||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void ReplacedTexture::PurgeIfOlder(double t) {
|
||||
if (lastUsed_ < t && !threadWaitable_) {
|
||||
levelData_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
ReplacedTexture::~ReplacedTexture() {
|
||||
if (threadWaitable_) {
|
||||
cancelPrepare_ = true;
|
||||
threadWaitable_->WaitAndRelease();
|
||||
threadWaitable_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ReplacedTexture::Load(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 (levelData_.empty())
|
||||
return false;
|
||||
|
||||
const ReplacedTextureLevel &info = levels_[level];
|
||||
const std::vector<uint8_t> &data = levelData_[level];
|
||||
|
||||
if (data.empty())
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
class IniFile;
|
||||
class TextureCacheCommon;
|
||||
class TextureReplacer;
|
||||
class ReplacedTextureTask;
|
||||
class LimitedWaitable;
|
||||
|
||||
enum class ReplacedTextureFormat {
|
||||
F_5650,
|
||||
|
@ -125,6 +127,8 @@ namespace std {
|
|||
}
|
||||
|
||||
struct ReplacedTexture {
|
||||
~ReplacedTexture();
|
||||
|
||||
inline bool Valid() {
|
||||
return !levels_.empty();
|
||||
}
|
||||
|
@ -153,13 +157,24 @@ struct ReplacedTexture {
|
|||
return (u8)alphaStatus_;
|
||||
}
|
||||
|
||||
bool IsReady(double budget);
|
||||
|
||||
bool Load(int level, void *out, int rowPitch);
|
||||
|
||||
protected:
|
||||
void Prepare();
|
||||
void PrepareData(int level);
|
||||
void PurgeIfOlder(double t);
|
||||
|
||||
std::vector<ReplacedTextureLevel> levels_;
|
||||
std::vector<std::vector<uint8_t>> levelData_;
|
||||
ReplacedTextureAlpha alphaStatus_;
|
||||
double lastUsed_ = 0.0;
|
||||
LimitedWaitable *threadWaitable_ = nullptr;
|
||||
bool cancelPrepare_ = false;
|
||||
|
||||
friend TextureReplacer;
|
||||
friend ReplacedTextureTask;
|
||||
};
|
||||
|
||||
struct ReplacedTextureDecodeInfo {
|
||||
|
@ -188,9 +203,14 @@ public:
|
|||
|
||||
ReplacedTexture &FindReplacement(u64 cachekey, u32 hash, int w, int h);
|
||||
bool FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering);
|
||||
ReplacedTexture &FindNone() {
|
||||
return none_;
|
||||
}
|
||||
|
||||
void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h);
|
||||
|
||||
void Decimate(bool forcePressure);
|
||||
|
||||
static bool GenerateIni(const std::string &gameID, Path &generatedFilename);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Common/Profiler/Profiler.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Debugger/MemBlockInfo.h"
|
||||
#include "Core/Reporting.h"
|
||||
|
@ -472,6 +473,15 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
|
|||
reason = "scaling";
|
||||
}
|
||||
}
|
||||
if (match && (entry->status & TexCacheEntry::STATUS_TO_REPLACE) && replacementTimeThisFrame_ < replacementFrameBudget_) {
|
||||
int w0 = gstate.getTextureWidth(0);
|
||||
int h0 = gstate.getTextureHeight(0);
|
||||
ReplacedTexture &replaced = FindReplacement(entry, w0, h0);
|
||||
if (replaced.Valid()) {
|
||||
match = false;
|
||||
reason = "replacing";
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
// got one!
|
||||
|
@ -720,6 +730,7 @@ void TextureCacheCommon::Decimate(bool forcePressure) {
|
|||
}
|
||||
|
||||
DecimateVideos();
|
||||
replacer_.Decimate(forcePressure);
|
||||
}
|
||||
|
||||
void TextureCacheCommon::DecimateVideos() {
|
||||
|
@ -1263,6 +1274,29 @@ u32 TextureCacheCommon::EstimateTexMemoryUsage(const TexCacheEntry *entry) {
|
|||
return pixelSize << (dimW + dimH);
|
||||
}
|
||||
|
||||
ReplacedTexture &TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &w, int &h) {
|
||||
// Allow some delay to reduce pop-in.
|
||||
constexpr double MAX_BUDGET_PER_TEX = 0.25 / 60.0;
|
||||
|
||||
double replaceStart = time_now_d();
|
||||
u64 cachekey = replacer_.Enabled() ? entry->CacheKey() : 0;
|
||||
ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h);
|
||||
if (replaced.IsReady(std::min(MAX_BUDGET_PER_TEX, replacementFrameBudget_ - replacementTimeThisFrame_))) {
|
||||
if (replaced.GetSize(0, w, h)) {
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
|
||||
// Consider it already "scaled" and remove any delayed replace flag.
|
||||
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
|
||||
entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE;
|
||||
return replaced;
|
||||
}
|
||||
} else if (replaced.Valid()) {
|
||||
entry->status |= TexCacheEntry::STATUS_TO_REPLACE;
|
||||
}
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
return replacer_.FindNone();
|
||||
}
|
||||
|
||||
static void ReverseColors(void *dstBuf, const void *srcBuf, GETextureFormat fmt, int numPixels, bool useBGRA) {
|
||||
switch (fmt) {
|
||||
case GE_TFMT_4444:
|
||||
|
|
|
@ -123,16 +123,17 @@ struct TexCacheEntry {
|
|||
STATUS_CLUT_RECHECK = 0x20, // Another texture with same addr had a hashfail.
|
||||
STATUS_TO_SCALE = 0x80, // Pending texture scaling in a later frame.
|
||||
STATUS_IS_SCALED = 0x100, // Has been scaled (can't be replaceImages'd.)
|
||||
STATUS_TO_REPLACE = 0x0200, // Pending texture replacement.
|
||||
// When hashing large textures, we optimize 512x512 down to 512x272 by default, since this
|
||||
// is commonly the only part accessed. If access is made above 272, we hash the entire
|
||||
// texture, and set this flag to allow scaling the texture just once for the new hash.
|
||||
STATUS_FREE_CHANGE = 0x200, // Allow one change before marking "frequent".
|
||||
STATUS_FREE_CHANGE = 0x0400, // Allow one change before marking "frequent".
|
||||
|
||||
STATUS_BAD_MIPS = 0x400, // Has bad or unusable mipmap levels.
|
||||
STATUS_BAD_MIPS = 0x0800, // Has bad or unusable mipmap levels.
|
||||
|
||||
STATUS_FRAMEBUFFER_OVERLAP = 0x800,
|
||||
STATUS_FRAMEBUFFER_OVERLAP = 0x1000,
|
||||
|
||||
STATUS_FORCE_REBUILD = 0x1000,
|
||||
STATUS_FORCE_REBUILD = 0x2000,
|
||||
};
|
||||
|
||||
// Status, but int so we can zero initialize.
|
||||
|
@ -277,6 +278,7 @@ protected:
|
|||
void DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool useBGRA, bool expandTo32Bit);
|
||||
void UnswizzleFromMem(u32 *dest, u32 destPitch, const u8 *texptr, u32 bufw, u32 height, u32 bytesPerPixel);
|
||||
void ReadIndexedTex(u8 *out, int outPitch, int level, const u8 *texptr, int bytesPerIndex, int bufw, bool expandTo32Bit);
|
||||
ReplacedTexture &FindReplacement(TexCacheEntry *entry, int &w, int &h);
|
||||
|
||||
template <typename T>
|
||||
inline const T *GetCurrentClut() {
|
||||
|
@ -334,6 +336,9 @@ protected:
|
|||
int decimationCounter_;
|
||||
int texelsScaledThisFrame_ = 0;
|
||||
int timesInvalidatedAllThisFrame_ = 0;
|
||||
double replacementTimeThisFrame_ = 0;
|
||||
// TODO: Maybe vary by FPS...
|
||||
double replacementFrameBudget_ = 0.5 / 60.0;
|
||||
|
||||
TexCache cache_;
|
||||
u32 cacheSizeEstimate_ = 0;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <d3d11.h>
|
||||
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
|
@ -169,6 +170,7 @@ void TextureCacheD3D11::InvalidateLastTexture() {
|
|||
void TextureCacheD3D11::StartFrame() {
|
||||
InvalidateLastTexture();
|
||||
timesInvalidatedAllThisFrame_ = 0;
|
||||
replacementTimeThisFrame_ = 0.0;
|
||||
|
||||
if (texelsScaledThisFrame_) {
|
||||
// INFO_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_);
|
||||
|
@ -483,14 +485,12 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
|||
scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1);
|
||||
}
|
||||
|
||||
u64 cachekey = replacer_.Enabled() ? entry->CacheKey() : 0;
|
||||
int w = gstate.getTextureWidth(0);
|
||||
int h = gstate.getTextureHeight(0);
|
||||
ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h);
|
||||
if (replaced.GetSize(0, w, h)) {
|
||||
ReplacedTexture &replaced = FindReplacement(entry, w, h);
|
||||
if (replaced.Valid()) {
|
||||
// We're replacing, so we won't scale.
|
||||
scaleFactor = 1;
|
||||
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
|
||||
maxLevel = replaced.MaxLevel();
|
||||
badMipSizes = false;
|
||||
}
|
||||
|
@ -678,7 +678,9 @@ void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &
|
|||
if (replaced.GetSize(level, w, h)) {
|
||||
mapData = (u32 *)AllocateAlignedMemory(w * h * sizeof(u32), 16);
|
||||
mapRowPitch = w * 4;
|
||||
double replaceStart = time_now_d();
|
||||
replaced.Load(level, mapData, mapRowPitch);
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
dstFmt = ToDXGIFormat(replaced.Format(level));
|
||||
} else {
|
||||
GETextureFormat tfmt = (GETextureFormat)entry.format;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
|
@ -134,6 +135,7 @@ void TextureCacheDX9::ApplySamplingParams(const SamplerCacheKey &key) {
|
|||
void TextureCacheDX9::StartFrame() {
|
||||
InvalidateLastTexture();
|
||||
timesInvalidatedAllThisFrame_ = 0;
|
||||
replacementTimeThisFrame_ = 0.0;
|
||||
|
||||
if (texelsScaledThisFrame_) {
|
||||
VERBOSE_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_);
|
||||
|
@ -438,14 +440,12 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) {
|
|||
scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1);
|
||||
}
|
||||
|
||||
u64 cachekey = replacer_.Enabled() ? entry->CacheKey() : 0;
|
||||
int w = gstate.getTextureWidth(0);
|
||||
int h = gstate.getTextureHeight(0);
|
||||
ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h);
|
||||
if (replaced.GetSize(0, w, h)) {
|
||||
ReplacedTexture &replaced = FindReplacement(entry, w, h);
|
||||
if (replaced.Valid()) {
|
||||
// We're replacing, so we won't scale.
|
||||
scaleFactor = 1;
|
||||
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
|
||||
maxLevel = replaced.MaxLevel();
|
||||
badMipSizes = false;
|
||||
}
|
||||
|
@ -615,7 +615,9 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re
|
|||
|
||||
gpuStats.numTexturesDecoded++;
|
||||
if (replaced.GetSize(level, w, h)) {
|
||||
double replaceStart = time_now_d();
|
||||
replaced.Load(level, rect.pBits, rect.Pitch);
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
dstFmt = ToD3D9Format(replaced.Format(level));
|
||||
} else {
|
||||
GETextureFormat tfmt = (GETextureFormat)entry.format;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "Common/Math/math_util.h"
|
||||
#include "Common/Profiler/Profiler.h"
|
||||
#include "Common/GPU/OpenGL/GLRenderManager.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Host.h"
|
||||
|
@ -152,6 +153,7 @@ static void ConvertColors(void *dstBuf, const void *srcBuf, Draw::DataFormat dst
|
|||
void TextureCacheGLES::StartFrame() {
|
||||
InvalidateLastTexture();
|
||||
timesInvalidatedAllThisFrame_ = 0;
|
||||
replacementTimeThisFrame_ = 0.0;
|
||||
|
||||
GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
if (!lowMemoryMode_ && renderManager->SawOutOfMemory()) {
|
||||
|
@ -502,14 +504,12 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
|
|||
scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1);
|
||||
}
|
||||
|
||||
u64 cachekey = replacer_.Enabled() ? entry->CacheKey() : 0;
|
||||
int w = gstate.getTextureWidth(0);
|
||||
int h = gstate.getTextureHeight(0);
|
||||
ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h);
|
||||
if (replaced.GetSize(0, w, h)) {
|
||||
ReplacedTexture &replaced = FindReplacement(entry, w, h);
|
||||
if (replaced.Valid()) {
|
||||
// We're replacing, so we won't scale.
|
||||
scaleFactor = 1;
|
||||
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
|
||||
maxLevel = replaced.MaxLevel();
|
||||
badMipSizes = false;
|
||||
}
|
||||
|
@ -658,7 +658,9 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r
|
|||
int bpp = replaced.Format(level) == ReplacedTextureFormat::F_8888 ? 4 : 2;
|
||||
decPitch = w * bpp;
|
||||
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(decPitch * h, 16);
|
||||
double replaceStart = time_now_d();
|
||||
replaced.Load(level, rearrange, decPitch);
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
pixelData = rearrange;
|
||||
|
||||
dstFmt = ToDataFormat(replaced.Format(level));
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "Common/Data/Convert/ColorConv.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/MemMap.h"
|
||||
|
@ -442,6 +443,7 @@ void TextureCacheVulkan::StartFrame() {
|
|||
|
||||
timesInvalidatedAllThisFrame_ = 0;
|
||||
texelsScaledThisFrame_ = 0;
|
||||
replacementTimeThisFrame_ = 0.0;
|
||||
|
||||
if (clearCacheNextFrame_) {
|
||||
Clear(true);
|
||||
|
@ -773,14 +775,12 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
|||
scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1);
|
||||
}
|
||||
|
||||
u64 cachekey = replacer_.Enabled() ? entry->CacheKey() : 0;
|
||||
int w = gstate.getTextureWidth(0);
|
||||
int h = gstate.getTextureHeight(0);
|
||||
ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h);
|
||||
if (replaced.GetSize(0, w, h)) {
|
||||
ReplacedTexture &replaced = FindReplacement(entry, w, h);
|
||||
if (replaced.Valid()) {
|
||||
// We're replacing, so we won't scale.
|
||||
scaleFactor = 1;
|
||||
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
|
||||
maxLevel = replaced.MaxLevel();
|
||||
badMipSizes = false;
|
||||
}
|
||||
|
@ -899,7 +899,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
|||
|
||||
ReplacedTextureDecodeInfo replacedInfo;
|
||||
if (replacer_.Enabled() && !replaced.Valid()) {
|
||||
replacedInfo.cachekey = cachekey;
|
||||
replacedInfo.cachekey = entry->CacheKey();
|
||||
replacedInfo.hash = entry->fullhash;
|
||||
replacedInfo.addr = entry->addr;
|
||||
replacedInfo.isVideo = IsVideo(entry->addr);
|
||||
|
@ -934,7 +934,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
|||
if (replaced.Valid()) {
|
||||
// Directly load the replaced image.
|
||||
data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment);
|
||||
double replaceStart = time_now_d();
|
||||
replaced.Load(i, data, stride); // if it fails, it'll just be garbage data... OK for now.
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
|
||||
} else {
|
||||
auto dispatchCompute = [&](VkDescriptorSet descSet) {
|
||||
|
|
Loading…
Add table
Reference in a new issue