mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Replacement: Use a thread to load tex replacements.
This commit is contained in:
parent
ee882d1861
commit
09f0578a64
4 changed files with 97 additions and 20 deletions
|
@ -743,24 +743,86 @@ float TextureReplacer::LookupReduceHashRange(int& w, int& h) {
|
|||
}
|
||||
}
|
||||
|
||||
class LimitedWaitable : public Waitable {
|
||||
public:
|
||||
LimitedWaitable() {}
|
||||
|
||||
void Wait() override {
|
||||
if (!triggered_) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cond_.wait(lock, [&] { return !triggered_; });
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitFor(uint32_t us) {
|
||||
if (!triggered_) {
|
||||
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_;
|
||||
bool triggered_ = false;
|
||||
};
|
||||
|
||||
class ReplacedTextureTask : public Task {
|
||||
public:
|
||||
ReplacedTextureTask(ReplacedTexture &tex, LimitedWaitable *w) : tex_(tex), waitable_(w) {
|
||||
}
|
||||
|
||||
void Run() override {
|
||||
for (int i = (int)tex_.levelData_.size(); i <= tex_.MaxLevel(); ++i) {
|
||||
tex_.levelData_.resize(i + 1);
|
||||
tex_.PrepareData(i);
|
||||
}
|
||||
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_.size() == levels_.size())
|
||||
return Valid();
|
||||
if (budget <= 0.0)
|
||||
return false;
|
||||
|
||||
double deadline = lastUsed_ + budget;
|
||||
for (int i = (int)levelData_.size(); i <= MaxLevel(); ++i) {
|
||||
levelData_.resize(i + 1);
|
||||
PrepareData(i);
|
||||
if (time_now_d() >= deadline) {
|
||||
break;
|
||||
}
|
||||
threadWaitable_ = new LimitedWaitable();
|
||||
g_threadManager.EnqueueTask(new ReplacedTextureTask(*this, threadWaitable_), TaskType::IO_BLOCKING);
|
||||
|
||||
uint32_t budget_us = (uint32_t)(budget * 1000000.0);
|
||||
if (threadWaitable_->WaitFor(budget_us)) {
|
||||
threadWaitable_->WaitAndRelease();
|
||||
threadWaitable_ = nullptr;
|
||||
|
||||
// If we finished all the levels, we're done.
|
||||
return levelData_.size() == levels_.size();
|
||||
}
|
||||
// If we finished all the levels, we're done.
|
||||
return levelData_.size() == levels_.size();
|
||||
|
||||
// Still pending on thread.
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReplacedTexture::PrepareData(int level) {
|
||||
|
@ -794,18 +856,26 @@ void ReplacedTexture::PrepareData(int level) {
|
|||
int w, h, f;
|
||||
uint8_t *image;
|
||||
if (LoadZIMPtr(&zim[0], zimSize, &w, &h, &f, &image)) {
|
||||
if (w != info.w || h != info.h) {
|
||||
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(w * h * 4);
|
||||
ParallelMemcpy(&g_threadManager, &out[0], image, info.w * 4 * info.h);
|
||||
out.resize(info.w * info.h * 4);
|
||||
if (w == info.w) {
|
||||
ParallelMemcpy(&g_threadManager, &out[0], image, info.w * 4 * info.h);
|
||||
} else {
|
||||
ParallelRangeLoop(&g_threadManager, [&](int l, int u) {
|
||||
for (int y = l; y < u; ++y) {
|
||||
memcpy(&out[info.w * 4 * y], image + w * 4 * y, w * 4);
|
||||
}
|
||||
}, 0, h, 4);
|
||||
}
|
||||
free(image);
|
||||
}
|
||||
|
||||
CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)&out[0], w, w, h);
|
||||
CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)&out[0], info.w, w, h);
|
||||
if (res == CHECKALPHA_ANY || level == 0) {
|
||||
alphaStatus_ = ReplacedTextureAlpha(res);
|
||||
}
|
||||
|
@ -818,7 +888,7 @@ void ReplacedTexture::PrepareData(int level) {
|
|||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
if (png.width != info.w || png.height != info.h) {
|
||||
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;
|
||||
|
@ -834,8 +904,8 @@ void ReplacedTexture::PrepareData(int level) {
|
|||
}
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
|
||||
out.resize(png.width * png.height * 4);
|
||||
if (!png_image_finish_read(&png, nullptr, &out[0], png.width * 4, 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);
|
||||
out.resize(0);
|
||||
|
@ -845,7 +915,7 @@ void ReplacedTexture::PrepareData(int level) {
|
|||
|
||||
if (!checkedAlpha) {
|
||||
// This will only check the hashed bits.
|
||||
CheckAlphaResult res = CheckAlphaRGBA8888Basic((u32 *)&out[0], png.width, 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);
|
||||
}
|
||||
|
@ -856,7 +926,7 @@ void ReplacedTexture::PrepareData(int level) {
|
|||
}
|
||||
|
||||
void ReplacedTexture::PurgeIfOlder(double t) {
|
||||
if (lastUsed_ < t) {
|
||||
if (lastUsed_ < t && !threadWaitable_) {
|
||||
levelData_.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
class IniFile;
|
||||
class TextureCacheCommon;
|
||||
class TextureReplacer;
|
||||
class ReplacedTextureTask;
|
||||
class LimitedWaitable;
|
||||
|
||||
enum class ReplacedTextureFormat {
|
||||
F_5650,
|
||||
|
@ -165,8 +167,10 @@ protected:
|
|||
std::vector<std::vector<uint8_t>> levelData_;
|
||||
ReplacedTextureAlpha alphaStatus_;
|
||||
double lastUsed_ = 0.0;
|
||||
LimitedWaitable * threadWaitable_ = nullptr;
|
||||
|
||||
friend TextureReplacer;
|
||||
friend ReplacedTextureTask;
|
||||
};
|
||||
|
||||
struct ReplacedTextureDecodeInfo {
|
||||
|
|
|
@ -1275,10 +1275,13 @@ u32 TextureCacheCommon::EstimateTexMemoryUsage(const TexCacheEntry *entry) {
|
|||
}
|
||||
|
||||
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(replacementFrameBudget_ - replacementTimeThisFrame_)) {
|
||||
if (replaced.IsReady(std::min(MAX_BUDGET_PER_TEX, replacementFrameBudget_ - replacementTimeThisFrame_))) {
|
||||
if (replaced.GetSize(0, w, h)) {
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
|
||||
|
|
|
@ -338,7 +338,7 @@ protected:
|
|||
int timesInvalidatedAllThisFrame_ = 0;
|
||||
double replacementTimeThisFrame_ = 0;
|
||||
// TODO: Maybe vary by FPS...
|
||||
double replacementFrameBudget_ = 0.75 / 60.0;
|
||||
double replacementFrameBudget_ = 0.5 / 60.0;
|
||||
|
||||
TexCache cache_;
|
||||
u32 cacheSizeEstimate_ = 0;
|
||||
|
|
Loading…
Add table
Reference in a new issue