diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp index d8803a5f8a..3cd62638af 100644 --- a/GPU/Common/ReplacedTexture.cpp +++ b/GPU/Common/ReplacedTexture.cpp @@ -245,19 +245,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 &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; } @@ -265,7 +261,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; @@ -290,7 +285,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: @@ -302,7 +296,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. @@ -329,10 +322,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 { @@ -362,8 +351,9 @@ 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; @@ -393,6 +383,8 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string vfs_->Rewind(openFile); + level.fileRef = fileRef; + if (imageType == ReplacedImageType::DDS) { DDSHeader header; DDSHeaderDXT10 header10{}; @@ -400,13 +392,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 &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 zim(new uint8_t[fileSize]); if (!zim) { ERROR_LOG(G3D, "Failed to allocate memory for texture replacement"); @@ -424,6 +435,7 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string int w, h, f; uint8_t *image; + std::vector &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()); @@ -441,11 +453,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 = {}; @@ -477,6 +492,7 @@ bool ReplacedTexture::LoadLevelData(VFSFileReference *fileRef, const std::string } png.format = PNG_FORMAT_RGBA; + std::vector &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); @@ -494,15 +510,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) { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 20a8345c84..6417bb8491 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -584,11 +584,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 +600,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) {