mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #17103 from hrydgard/replacement-dds-mipmaps
Texture replacement: Load DDS mipmaps
This commit is contained in:
commit
7df51c3d06
5 changed files with 54 additions and 40 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue