Merge pull request #15884 from unknownbrackets/texreplace-io-async

Replacement: Read files only within time budget
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2022-08-22 11:10:16 +02:00
commit 3c307c9857
4 files changed
+67 -30

No files matched your search

+24 -10
View File
@@ -49,7 +49,8 @@ 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.
TextureReplacer::TextureReplacer() {
none_.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN;
none_.initDone_ = true;
none_.prepareDone_ = true;
}
TextureReplacer::~TextureReplacer() {
@@ -373,7 +374,7 @@ 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, double budget) {
// Only actually replace if we're replacing. We might just be saving.
if (!Enabled() || !g_Config.bReplaceTextures) {
return none_;
@@ -382,13 +383,18 @@ ReplacedTexture &TextureReplacer::FindReplacement(u64 cachekey, u32 hash, int w,
ReplacementCacheKey replacementKey(cachekey, hash);
auto it = cache_.find(replacementKey);
if (it != cache_.end()) {
if (!it->second.prepareDone_ && budget > 0.0) {
// We don't do this on a thread, but we only do it while within budget.
PopulateReplacement(&it->second, cachekey, hash, w, h);
}
return it->second;
}
// Okay, let's construct the result.
ReplacedTexture &result = cache_[replacementKey];
result.alphaStatus_ = ReplacedTextureAlpha::UNKNOWN;
PopulateReplacement(&result, cachekey, hash, w, h);
if (!g_Config.bReplaceTexturesAllowLate || budget > 0.0) {
PopulateReplacement(&result, cachekey, hash, w, h);
}
return result;
}
@@ -433,7 +439,7 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey,
break;
}
result->alphaStatus_ = ReplacedTextureAlpha::UNKNOWN;
result->prepareDone_ = true;
}
enum class ReplacedImageType {
@@ -815,11 +821,13 @@ bool ReplacedTexture::IsReady(double budget) {
}
// Loaded already, or not yet on a thread?
if (!levelData_.empty())
if (initDone_ && !levelData_.empty())
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 (g_Config.bReplaceTexturesAllowLate) {
if (threadWaitable_)
@@ -829,10 +837,11 @@ bool ReplacedTexture::IsReady(double budget) {
if (threadWaitable_->WaitFor(budget)) {
// If we finished all the levels, we're done.
return !levelData_.empty();
return initDone_ && !levelData_.empty();
}
} else {
Prepare();
_assert_(initDone_);
return true;
}
@@ -842,16 +851,19 @@ bool ReplacedTexture::IsReady(double budget) {
void ReplacedTexture::Prepare() {
std::unique_lock<std::mutex> lock(mutex_);
if (cancelPrepare_)
if (cancelPrepare_) {
initDone_ = true;
return;
}
levelData_.resize(NumLevels());
for (int i = 0; i < NumLevels(); ++i) {
levelData_.resize(levels_.size());
for (int i = 0; i < (int)levels_.size(); ++i) {
if (cancelPrepare_)
break;
PrepareData(i);
}
initDone_ = true;
if (!cancelPrepare_ && threadWaitable_)
threadWaitable_->Notify();
}
@@ -975,6 +987,8 @@ 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 (!initDone_)
return false;
if (levelData_.empty())
return false;
+20 -4
View File
@@ -118,10 +118,20 @@ 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;
@@ -131,12 +141,16 @@ struct ReplacedTexture {
}
int NumLevels() const {
if (!initDone_)
return 0;
return (int)levels_.size();
}
Draw::DataFormat Format(int level) const {
if ((size_t)level < levels_.size()) {
return levels_[level].fmt;
if (initDone_) {
if ((size_t)level < levels_.size()) {
return levels_[level].fmt;
}
}
return Draw::DataFormat::R8G8B8A8_UNORM;
}
@@ -156,11 +170,13 @@ protected:
std::vector<ReplacedTextureLevel> levels_;
std::vector<std::vector<uint8_t>> levelData_;
ReplacedTextureAlpha alphaStatus_;
ReplacedTextureAlpha alphaStatus_ = ReplacedTextureAlpha::UNKNOWN;
double lastUsed_ = 0.0;
LimitedWaitable *threadWaitable_ = nullptr;
std::mutex mutex_;
bool cancelPrepare_ = false;
bool initDone_ = false;
bool prepareDone_ = false;
friend TextureReplacer;
friend ReplacedTextureTask;
@@ -196,7 +212,7 @@ public:
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, double budget);
bool FindFiltering(u64 cachekey, u32 hash, TextureFiltering *forceFiltering);
ReplacedTexture &FindNone() {
return none_;
+18 -11
View File
@@ -482,7 +482,14 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
int h0 = gstate.getTextureHeight(0);
int d0 = 1;
ReplacedTexture &replaced = FindReplacement(entry, w0, h0, d0);
if (replaced.Valid()) {
if (replaced.IsInvalid()) {
entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE;
if (g_Config.bSaveNewTextures) {
// Load once more to actually save.
match = false;
reason = "replacing";
}
} else {
match = false;
reason = "replacing";
}
@@ -1303,22 +1310,22 @@ ReplacedTexture &TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &
constexpr double MAX_BUDGET_PER_TEX = 0.25 / 60.0;
double replaceStart = time_now_d();
double budget = std::min(MAX_BUDGET_PER_TEX, replacementFrameBudget_ - replacementTimeThisFrame_);
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_))) {
ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h, budget);
if (replaced.IsReady(budget)) {
if (replaced.GetSize(0, w, h)) {
replacementTimeThisFrame_ += time_now_d() - replaceStart;
// Consider it already "scaled" and remove any delayed replace flag.
// Consider it already "scaled."
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE;
return replaced;
}
} else if (replaced.Valid()) {
// Remove the flag, even if it was invalid.
entry->status &= ~TexCacheEntry::STATUS_TO_REPLACE;
} else if (!replaced.IsInvalid()) {
entry->status |= TexCacheEntry::STATUS_TO_REPLACE;
}
replacementTimeThisFrame_ += time_now_d() - replaceStart;
return replacer_.FindNone();
return replaced;
}
// This is only used in the GLES backend, where we don't point these to video memory.
@@ -2375,7 +2382,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i
}
}
if (replacer_.Enabled()) {
if (replacer_.Enabled() && replaced.IsInvalid()) {
ReplacedTextureDecodeInfo replacedInfo;
replacedInfo.cachekey = entry.CacheKey();
replacedInfo.hash = entry.fullhash;
+5 -5
View File
@@ -464,7 +464,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
// Any texture scaling is gonna move away from the original 16-bit format, if any.
VkFormat actualFmt = plan.scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt;
if (plan.replaced->Valid()) {
actualFmt = ToVulkanFormat(plan.replaced->Format(0));
actualFmt = ToVulkanFormat(plan.replaced->Format(plan.baseLevelSrc));
}
bool computeUpload = false;
@@ -536,7 +536,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
ReplacedTextureDecodeInfo replacedInfo;
bool willSaveTex = false;
if (replacer_.Enabled() && !plan.replaced->Valid() && plan.depth == 1) {
if (replacer_.Enabled() && plan.replaced->IsInvalid() && plan.depth == 1) {
replacedInfo.cachekey = entry->CacheKey();
replacedInfo.hash = entry->fullhash;
replacedInfo.addr = entry->addr;
@@ -565,7 +565,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
int mipWidth = mipUnscaledWidth * plan.scaleFactor;
int mipHeight = mipUnscaledHeight * plan.scaleFactor;
if (plan.replaced->Valid()) {
plan.replaced->GetSize(i, mipWidth, mipHeight);
plan.replaced->GetSize(plan.baseLevelSrc + i, mipWidth, mipHeight);
}
int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; // output bpp
@@ -595,7 +595,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
// Directly load the replaced image.
data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment);
double replaceStart = time_now_d();
plan.replaced->Load(i, data, stride); // if it fails, it'll just be garbage data... OK for now.
plan.replaced->Load(plan.baseLevelSrc + i, data, stride); // if it fails, it'll just be garbage data... OK for now.
replacementTimeThisFrame_ += time_now_d() - replaceStart;
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT,
"Copy Upload (replaced): %dx%d", mipWidth, mipHeight);
@@ -636,7 +636,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
int w = dataScaled ? mipWidth : mipUnscaledWidth;
int h = dataScaled ? mipHeight : mipUnscaledHeight;
// At this point, data should be saveData, and not slow.
replacer_.NotifyTextureDecoded(replacedInfo, data, stride, i, w, h);
replacer_.NotifyTextureDecoded(replacedInfo, data, stride, plan.baseLevelSrc + i, w, h);
}
}
}