mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-11 05:26:24 +02:00
Merge pull request #15884 from unknownbrackets/texreplace-io-async
Replacement: Read files only within time budget
This commit is contained in:
4 files changed
+67
-30
No files matched your search
+24
-10
@@ -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
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user