Skip to content

Commit

Permalink
Replacement: Use a thread to load tex replacements.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Oct 22, 2021
1 parent ee882d1 commit 09f0578
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 20 deletions.
106 changes: 88 additions & 18 deletions Core/TextureReplacer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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);
}
Expand All @@ -856,7 +926,7 @@ void ReplacedTexture::PrepareData(int level) {
}

void ReplacedTexture::PurgeIfOlder(double t) {
if (lastUsed_ < t) {
if (lastUsed_ < t && !threadWaitable_) {
levelData_.clear();
}
}
Expand Down
4 changes: 4 additions & 0 deletions Core/TextureReplacer.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
class IniFile;
class TextureCacheCommon;
class TextureReplacer;
class ReplacedTextureTask;
class LimitedWaitable;

enum class ReplacedTextureFormat {
F_5650,
Expand Down Expand Up @@ -165,8 +167,10 @@ struct ReplacedTexture {
std::vector<std::vector<uint8_t>> levelData_;
ReplacedTextureAlpha alphaStatus_;
double lastUsed_ = 0.0;
LimitedWaitable * threadWaitable_ = nullptr;

friend TextureReplacer;
friend ReplacedTextureTask;
};

struct ReplacedTextureDecodeInfo {
Expand Down
5 changes: 4 additions & 1 deletion GPU/Common/TextureCacheCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion GPU/Common/TextureCacheCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ class TextureCacheCommon {
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;
Expand Down

0 comments on commit 09f0578

Please sign in to comment.