#pragma once
#include <mutex>
#include <string>
#include "Common/File/VFS/VFS.h"
#include "Common/GPU/thin3d.h"
#include "Core/ConfigValues.h"
class TextureReplacer;
class LimitedWaitable;
enum class ReplacedTextureAlpha {
UNKNOWN = 0x04,
FULL = 0x00,
};
enum class ReplacedTextureHash {
QUICK,
XXH32,
XXH64,
};
enum class ReplacedImageType {
PNG,
ZIM,
DDS,
BASIS,
KTX2,
INVALID,
};
static const int MAX_REPLACEMENT_MIP_LEVELS = 12;
enum class ReplacementState : uint32_t {
UNLOADED,
PENDING,
NOT_FOUND,
ACTIVE,
CANCEL_INIT,
COUNT,
};
const char *StateString(ReplacementState state);
struct GPUFormatSupport {
bool bc123;
bool astc;
bool bc7;
bool etc2;
};
struct ReplacementDesc {
int newW;
int newH;
uint64_t cachekey;
uint32_t hash;
int w;
int h;
TextureFiltering forceFiltering;
std::string hashfiles;
Path basePath;
std::vector<std::string> filenames;
std::string logId;
GPUFormatSupport formatSupport;
};
class ReplacedTexture;
struct ReplacedTextureRef {
ReplacedTexture *texture;
std::string hashfiles;
};
struct ReplacedTextureLevel {
int w = 0;
int h = 0;
int fullW = 0;
int fullH = 0;
int fullDataSize = 0;
VFSFileReference *fileRef = nullptr;
};
class ReplacedTexture {
public:
ReplacedTexture(VFSBackend *vfs, const ReplacementDesc &desc);
~ReplacedTexture();
inline ReplacementState State() const {
return state_;
}
void SetState(ReplacementState state) {
_dbg_assert_(state != state_);
state_ = state;
}
void GetSize(int level, int *w, int *h) const {
_dbg_assert_(State() == ReplacementState::ACTIVE);
_dbg_assert_((size_t)level < levels_.size());
*w = levels_[level].fullW;
*h = levels_[level].fullH;
}
int GetLevelDataSizeAfterCopy(int level) const {
return levels_[level].fullDataSize;
}
size_t GetTotalDataSize() const {
if (State() != ReplacementState::ACTIVE) {
return 0;
}
size_t sz = 0;
for (auto &data : data_) {
sz += data.size();
}
return sz;
}
bool ForceFiltering(TextureFiltering *forceFiltering) const {
if (desc_.forceFiltering != (TextureFiltering)0) {
*forceFiltering = desc_.forceFiltering;
return true;
} else {
return false;
}
}
int NumLevels() const {
_dbg_assert_(State() == ReplacementState::ACTIVE);
return (int)levels_.size();
}
Draw::DataFormat Format() const {
_dbg_assert_(State() == ReplacementState::ACTIVE);
return fmt;
}
const ReplacementDesc &Desc() const {
return desc_;
}
u8 AlphaStatus() const {
return (u8)alphaStatus_;
}
bool Poll(double budget);
bool CopyLevelTo(int level, uint8_t *out, size_t outDataSize, int rowPitch);
std::string logId_;
private:
enum class LoadLevelResult {
LOAD_ERROR = 0,
CONTINUE = 1,
DONE = 2,
};
void Prepare(VFSBackend *vfs);
LoadLevelResult LoadLevelData(VFSFileReference *fileRef, const std::string &filename, int level, Draw::DataFormat *pixelFormat);
void PurgeIfNotUsedSinceTime(double t);
std::vector<std::vector<uint8_t>> data_;
std::vector<ReplacedTextureLevel> levels_;
double lastUsed_ = 0.0;
LimitedWaitable *threadWaitable_ = nullptr;
std::mutex lock_;
Draw::DataFormat fmt = Draw::DataFormat::UNDEFINED;
ReplacedTextureAlpha alphaStatus_ = ReplacedTextureAlpha::UNKNOWN;
double lastUsed = 0.0;
std::atomic<ReplacementState> state_ = ReplacementState::UNLOADED;
VFSBackend *vfs_ = nullptr;
ReplacementDesc desc_;
friend class TextureReplacer;
friend class ReplacedTextureTask;
};