Replacer: Perform the padding to pow2 size during CopyLevelTo, not during load. Saves memory.

This commit is contained in:
Henrik Rydgård 2023-03-17 13:08:35 +01:00
parent df41a5cebd
commit 79f4e73110
3 changed files with 32 additions and 11 deletions

View File

@ -250,7 +250,7 @@ void *AllocateAlignedMemory(size_t size, size_t alignment) {
#endif
#endif
_assert_msg_(ptr != nullptr, "Failed to allocate aligned memory");
_assert_msg_(ptr != nullptr, "Failed to allocate aligned memory of size %llu", size);
return ptr;
}

View File

@ -256,6 +256,12 @@ void ReplacedTexture::Prepare(VFSBackend *vfs) {
return;
}
// Update the level dimensions.
for (auto &level : levels_) {
level.fullW = (level.w * desc_.w) / desc_.newW;
level.fullH = (level.h * desc_.h) / desc_.newH;
}
SetState(ReplacementState::ACTIVE);
if (threadWaitable_)
@ -390,7 +396,6 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference
ERROR_LOG(G3D, "Could not load texture replacement info: %s - unsupported format %s", filename.c_str(), magic.c_str());
}
// Already populated from cache. TODO: Move this above the first read, and take level.w/h from the cache.
if (!data_[mipLevel].empty()) {
vfs_->CloseFile(openFile);
@ -398,13 +403,10 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference
return LoadLevelResult::DONE;
}
// Is this really the right place to do it?
level.w = (level.w * desc_.w) / desc_.newW;
level.h = (level.h * desc_.h) / desc_.newH;
if (good && mipLevel != 0) {
// Check that the mipmap size is correct. Can't load mips of the wrong size.
if (level.w != (levels_[0].w >> mipLevel) || level.h != (levels_[0].h >> mipLevel)) {
// If loading a low mip directly (through png most likely), check that the mipmap size is correct.
// Can't load mips of the wrong size.
if (level.w != std::max(1, (levels_[0].w >> mipLevel)) || level.h != std::max(1, (levels_[0].h >> mipLevel))) {
WARN_LOG(G3D, "Replacement mipmap invalid: size=%dx%d, expected=%dx%d (level %d)",
level.w, level.h, levels_[0].w >> mipLevel, levels_[0].h >> mipLevel, mipLevel);
good = false;
@ -668,6 +670,13 @@ bool ReplacedTexture::CopyLevelTo(int level, void *out, int rowPitch) {
return false;
}
// We pad the images right here during the copy.
// TODO: Add support for the texture cache to scale texture coordinates instead.
// It already supports this for render target textures that aren't powers of 2.
int outW = levels_[level].fullW;
int outH = levels_[level].fullH;
// We probably could avoid this lock, but better to play it safe.
std::lock_guard<std::mutex> guard(lock_);
@ -683,7 +692,7 @@ bool ReplacedTexture::CopyLevelTo(int level, void *out, int rowPitch) {
if (fmt == Draw::DataFormat::R8G8B8A8_UNORM) {
if (rowPitch < info.w * 4) {
ERROR_LOG(G3D, "Replacement rowPitch=%d, but w=%d (level=%d)", rowPitch, info.w * 4, level);
ERROR_LOG(G3D, "Replacement rowPitch=%d, but w=%d (level=%d) (too small)", rowPitch, info.w * 4, level);
return false;
}
@ -699,10 +708,18 @@ bool ReplacedTexture::CopyLevelTo(int level, void *out, int rowPitch) {
#ifdef PARALLEL_COPY
const int MIN_LINES_PER_THREAD = 4;
ParallelRangeLoop(&g_threadManager, [&](int l, int h) {
int extraPixels = outW - info.w;
for (int y = l; y < h; ++y) {
memcpy((uint8_t *)out + rowPitch * y, data.data() + info.w * 4 * y, info.w * 4);
// Fill the rest of the line with black.
memset((uint8_t *)out + rowPitch * y + info.w * 4, 0, extraPixels * 4);
}
}, 0, info.h, MIN_LINES_PER_THREAD);
// Memset the rest of the padding.
for (int y = info.h; y < outH; y++) {
uint8_t *dest = (uint8_t *)out + rowPitch * y;
memset(dest, 0, outW * 4);
}
#else
for (int y = 0; y < info.h; ++y) {
memcpy((uint8_t *)out + rowPitch * y, data.data() + info.w * 4 * y, info.w * 4);

View File

@ -93,8 +93,12 @@ struct ReplacedTextureRef {
// Metadata about a given texture level.
struct ReplacedTextureLevel {
// Data dimensions
int w = 0;
int h = 0;
// PSP texture dimensions
int fullW = 0;
int fullH = 0;
// To be able to reload, we need to be able to reopen, unfortunate we can't use zip_file_t.
// TODO: This really belongs on the level in the cache, not in the individual ReplacedTextureLevel objects.
@ -121,8 +125,8 @@ public:
void GetSize(int level, int *w, int *h) const {
_dbg_assert_(State() == ReplacementState::ACTIVE);
_dbg_assert_(level < levels_.size());
*w = levels_[level].w;
*h = levels_[level].h;
*w = levels_[level].fullW;
*h = levels_[level].fullH;
}
int GetLevelDataSize(int level) const {