Merge pull request #17103 from hrydgard/replacement-dds-mipmaps

Texture replacement: Load DDS mipmaps
This commit is contained in:
Henrik Rydgård 2023-03-13 09:58:32 +01:00 committed by GitHub
commit 7df51c3d06
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 40 deletions

View file

@ -111,8 +111,8 @@ void ParallelRangeLoop(ThreadManager *threadMan, const std::function<void(int, i
// NOTE: Supports a max of 2GB.
void ParallelMemcpy(ThreadManager *threadMan, void *dst, const void *src, size_t bytes, TaskPriority priority) {
// This threshold can probably be a lot bigger.
if (bytes < 512) {
// This threshold should be the same as the minimum split below, 128kb.
if (bytes < 128 * 1024) {
memcpy(dst, src, bytes);
return;
}
@ -129,7 +129,7 @@ void ParallelMemcpy(ThreadManager *threadMan, void *dst, const void *src, size_t
// NOTE: Supports a max of 2GB.
void ParallelMemset(ThreadManager *threadMan, void *dst, uint8_t value, size_t bytes, TaskPriority priority) {
// This threshold can probably be a lot bigger.
if (bytes < 512) {
if (bytes < 128 * 1024) {
memset(dst, 0, bytes);
return;
}

View file

@ -246,19 +246,15 @@ inline uint32_t RoundUpTo4(uint32_t value) {
// Returns true if Prepare should keep calling this to load more levels.
bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string &filename, int mipLevel, Draw::DataFormat *pixelFormat) {
ReplacedTextureLevel level;
level.fileRef = fileRef;
bool good = false;
if (levelData_->data.size() <= mipLevel) {
levelData_->data.resize(mipLevel + 1);
}
std::vector<uint8_t> &out = levelData_->data[mipLevel];
ReplacedTextureLevel level;
size_t fileSize;
VFSOpenFile *openFile = vfs_->OpenFileForRead(level.fileRef, &fileSize);
VFSOpenFile *openFile = vfs_->OpenFileForRead(fileRef, &fileSize);
if (!openFile) {
return false;
}
@ -266,7 +262,6 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
std::string magic;
ReplacedImageType imageType = Identify(vfs_, openFile, &magic);
int ddsBytesToRead = 0; // Used by the DDS reader only.
bool ddsDX10 = false;
int numMips = 1;
@ -291,7 +286,6 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
WARN_LOG(G3D, "BC1-3 formats not supported, skipping texture");
good = false;
}
ddsBytesToRead = RoundUpTo4(header.dwWidth) * RoundUpTo4(header.dwHeight); // 1 byte per pixel so this should be right.
*pixelFormat = Draw::DataFormat::BC7_UNORM_BLOCK;
break;
default:
@ -303,7 +297,6 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
WARN_LOG(G3D, "BC1-3 formats not supported");
good = false;
}
ddsBytesToRead = header.dwPitchOrLinearSize;
format = header.ddspf.dwFourCC;
// OK, there are a number of possible formats we might have ended up with. We choose just a few
// to support for now.
@ -330,10 +323,6 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
level.w = header.dwWidth;
level.h = header.dwHeight;
numMips = header.dwMipMapCount;
if (numMips > 1) {
WARN_LOG(G3D, "DDS file contains more than one mip level. Ignoring for now.");
}
} else if (imageType == ReplacedImageType::ZIM) {
uint32_t ignore = 0;
struct ZimHeader {
@ -363,8 +352,10 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
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 (!out.empty()) {
if (!levelData_->data[mipLevel].empty()) {
vfs_->CloseFile(openFile);
*pixelFormat = levelData_->fmt;
return true;
}
@ -383,6 +374,7 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
}
if (!good) {
vfs_->CloseFile(openFile);
return false;
}
@ -392,6 +384,8 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
vfs_->Rewind(openFile);
level.fileRef = fileRef;
if (imageType == ReplacedImageType::DDS) {
DDSHeader header;
DDSHeaderDXT10 header10{};
@ -399,13 +393,32 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
if (ddsDX10) {
vfs_->Read(openFile, &header10, sizeof(header10));
}
// For compressed formats (we don't support uncompressed DDS files yet), this is supposed to be the linear size.
out.resize(ddsBytesToRead);
size_t read_bytes = vfs_->Read(openFile, &out[0], ddsBytesToRead);
if (read_bytes != ddsBytesToRead) {
WARN_LOG(G3D, "DDS: Expected %d bytes, got %d", ddsBytesToRead, (int)read_bytes);
int blockSize = 0;
bool bc = Draw::DataFormatIsBlockCompressed(*pixelFormat, &blockSize);
_dbg_assert_(bc);
levelData_->data.resize(numMips);
// A DDS File can contain multiple mipmaps.
for (int i = 0; i < numMips; i++) {
std::vector<uint8_t> &out = levelData_->data[mipLevel + i];
int bytesToRead = RoundUpTo4(level.w) * RoundUpTo4(level.h) * blockSize / 16;
out.resize(bytesToRead);
size_t read_bytes = vfs_->Read(openFile, &out[0], bytesToRead);
if (read_bytes != bytesToRead) {
WARN_LOG(G3D, "DDS: Expected %d bytes, got %d", bytesToRead, (int)read_bytes);
}
levels_.push_back(level);
level.w /= 2;
level.h /= 2;
level.fileRef = nullptr; // We only provide a fileref on level 0 if we have mipmaps.
}
} else if (imageType == ReplacedImageType::ZIM) {
std::unique_ptr<uint8_t[]> zim(new uint8_t[fileSize]);
if (!zim) {
ERROR_LOG(G3D, "Failed to allocate memory for texture replacement");
@ -423,6 +436,7 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
int w, h, f;
uint8_t *image;
std::vector<uint8_t> &out = levelData_->data[mipLevel];
if (LoadZIMPtr(&zim[0], fileSize, &w, &h, &f, &image)) {
if (w > level.w || h > level.h) {
ERROR_LOG(G3D, "Texture replacement changed since header read: %s", filename.c_str());
@ -440,11 +454,14 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
}
}
free(image);
}
CheckAlphaResult res = CheckAlpha32Rect((u32 *)&out[0], level.w, w, h, 0xFF000000);
if (res == CHECKALPHA_ANY || mipLevel == 0) {
alphaStatus_ = ReplacedTextureAlpha(res);
CheckAlphaResult res = CheckAlpha32Rect((u32 *)&out[0], level.w, w, h, 0xFF000000);
if (res == CHECKALPHA_ANY || mipLevel == 0) {
alphaStatus_ = ReplacedTextureAlpha(res);
}
levels_.push_back(level);
} else {
good = false;
}
} else if (imageType == ReplacedImageType::PNG) {
png_image png = {};
@ -476,6 +493,7 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
}
png.format = PNG_FORMAT_RGBA;
std::vector<uint8_t> &out = levelData_->data[mipLevel];
out.resize(level.w * level.h * 4);
if (!png_image_finish_read(&png, nullptr, &out[0], level.w * 4, nullptr)) {
ERROR_LOG(G3D, "Could not load texture replacement: %s - %s", filename.c_str(), png.message);
@ -493,15 +511,15 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string
alphaStatus_ = ReplacedTextureAlpha(res);
}
}
levels_.push_back(level);
} else {
WARN_LOG(G3D, "Don't know how to load this image type! %d", (int)imageType);
}
cleanup();
levels_.push_back(level);
return true;
return good;
}
bool ReplacedTexture::CopyLevelTo(int level, void *out, int rowPitch) {
@ -544,8 +562,7 @@ bool ReplacedTexture::CopyLevelTo(int level, void *out, int rowPitch) {
}
} else {
// TODO: Add sanity checks here for other formats?
// Just gonna do a memcpy, slightly scared of the parallel ones.
memcpy(out, data.data(), data.size());
ParallelMemcpy(&g_threadManager, out, data.data(), data.size());
}
return true;

View file

@ -1551,8 +1551,8 @@ ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &
// Make sure we keep polling.
entry->status |= TexCacheEntry::STATUS_TO_REPLACE;
break;
default:
break;
default:
break;
}
replacementTimeThisFrame_ += time_now_d() - replaceStart;
return replaced;

View file

@ -301,7 +301,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
if (plan.replaceValid) {
int blockSize = 0;
if (Draw::DataFormatIsBlockCompressed(plan.replaced->Format(), &blockSize)) {
stride = mipWidth * 4; // This stride value doesn't quite make sense to me, but it works.
stride = ((mipWidth + 3) & ~3) * 4; // This stride value doesn't quite make sense to me, but it works.
dataSize = plan.replaced->GetLevelDataSize(i);
} else {
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format());

View file

@ -455,9 +455,6 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
Draw::DataFormat fmt = plan.replaced->Format();
bcFormat = Draw::DataFormatIsBlockCompressed(fmt, &bcAlign);
actualFmt = ToVulkanFormat(fmt);
if (actualFmt != VULKAN_8888_FORMAT) {
actualFmt = actualFmt;
}
}
bool computeUpload = false;
@ -584,11 +581,11 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
bool dataScaled = true;
if (plan.replaceValid) {
int bufferRowLength = byteStride;
int rowLength = pixelStride;
if (bcFormat) {
// For block compressed formats, we just set the upload size to the data size..
uploadSize = plan.replaced->GetLevelDataSize(plan.baseLevelSrc + i);
bufferRowLength = mipWidth;
rowLength = (mipWidth + 3) & ~3;
}
// Directly load the replaced image.
data = pushBuffer->PushAligned(uploadSize, &bufferOffset, &texBuf, pushAlignment);
@ -600,7 +597,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
replacementTimeThisFrame_ += time_now_d() - replaceStart;
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT,
"Copy Upload (replaced): %dx%d", mipWidth, mipHeight);
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, pixelStride);
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, rowLength);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
} else {
if (plan.depth != 1) {