From f7a0cae7f47c6fb9a80bf22ffd55281331c3aaef Mon Sep 17 00:00:00 2001 From: Stenzek <stenzek@gmail.com> Date: Tue, 22 May 2018 16:13:54 +1000 Subject: [PATCH] HiresTextures: Do not load compressed textures with unaligned dimensions D3D11 cannot handle block compressed textures where the first mip level is not a multiple of the block size. The simple fix for texture pack authors: leave these textures uncompressed. You can still use a .dds container. --- Source/Core/VideoCommon/HiresTextures.cpp | 2 +- Source/Core/VideoCommon/HiresTextures.h | 2 +- .../VideoCommon/HiresTextures_DDSLoader.cpp | 31 ++++++++++++++----- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Source/Core/VideoCommon/HiresTextures.cpp b/Source/Core/VideoCommon/HiresTextures.cpp index adca2551db..79e71fa32b 100644 --- a/Source/Core/VideoCommon/HiresTextures.cpp +++ b/Source/Core/VideoCommon/HiresTextures.cpp @@ -335,7 +335,7 @@ std::unique_ptr<HiresTexture> HiresTexture::Load(const std::string& base_filenam // Try loading DDS textures first, that way we maintain compression of DXT formats. // TODO: Reduce the number of open() calls here. We could use one fd. Level level; - if (!LoadDDSTexture(level, filename_iter->second.path)) + if (!LoadDDSTexture(level, filename_iter->second.path, mip_level)) { File::IOFile file; file.Open(filename_iter->second.path, "rb"); diff --git a/Source/Core/VideoCommon/HiresTextures.h b/Source/Core/VideoCommon/HiresTextures.h index af79a02d53..6d814b42a9 100644 --- a/Source/Core/VideoCommon/HiresTextures.h +++ b/Source/Core/VideoCommon/HiresTextures.h @@ -49,7 +49,7 @@ private: static std::unique_ptr<HiresTexture> Load(const std::string& base_filename, u32 width, u32 height); static bool LoadDDSTexture(HiresTexture* tex, const std::string& filename); - static bool LoadDDSTexture(Level& level, const std::string& filename); + static bool LoadDDSTexture(Level& level, const std::string& filename, u32 mip_level); static bool LoadTexture(Level& level, const std::vector<u8>& buffer); static void Prefetch(); diff --git a/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp b/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp index 5a3d7788ca..182f6fcbd5 100644 --- a/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp +++ b/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp @@ -12,6 +12,7 @@ #include "Common/Align.h" #include "Common/File.h" +#include "Common/Logging/Log.h" #include "Common/Swap.h" #include "VideoCommon/VideoConfig.h" @@ -400,9 +401,22 @@ bool ParseDDSHeader(File::IOFile& file, DDSLoadInfo* info) return true; } -bool ReadMipLevel(HiresTexture::Level* level, File::IOFile& file, const DDSLoadInfo& info, - u32 width, u32 height, u32 row_length, size_t size) +bool ReadMipLevel(HiresTexture::Level* level, File::IOFile& file, const std::string& filename, + u32 mip_level, const DDSLoadInfo& info, u32 width, u32 height, u32 row_length, + size_t size) { + // D3D11 cannot handle block compressed textures where the first mip level is + // not a multiple of the block size. + if (mip_level == 0 && info.block_size > 1 && + ((width % info.block_size) != 0 || (height % info.block_size) != 0)) + { + ERROR_LOG(VIDEO, + "Invalid dimensions for DDS texture %s. For compressed textures of this format, " + "the width/height of the first mip level must be a multiple of %u.", + filename.c_str(), info.block_size); + return false; + } + // Copy to the final storage location. level->width = width; level->height = height; @@ -435,8 +449,8 @@ bool HiresTexture::LoadDDSTexture(HiresTexture* tex, const std::string& filename // Read first mip level, as it may have a custom pitch. Level first_level; if (!file.Seek(info.first_mip_offset, SEEK_SET) || - !ReadMipLevel(&first_level, file, info, info.width, info.height, info.first_mip_row_length, - info.first_mip_size)) + !ReadMipLevel(&first_level, file, filename, 0, info, info.width, info.height, + info.first_mip_row_length, info.first_mip_size)) { return false; } @@ -458,7 +472,8 @@ bool HiresTexture::LoadDDSTexture(HiresTexture* tex, const std::string& filename u32 mip_row_length = blocks_wide * info.block_size; size_t mip_size = blocks_wide * static_cast<size_t>(info.bytes_per_block) * blocks_high; Level level; - if (!ReadMipLevel(&level, file, info, mip_width, mip_height, mip_row_length, mip_size)) + if (!ReadMipLevel(&level, file, filename, i, info, mip_width, mip_height, mip_row_length, + mip_size)) break; tex->m_levels.push_back(std::move(level)); @@ -467,7 +482,7 @@ bool HiresTexture::LoadDDSTexture(HiresTexture* tex, const std::string& filename return true; } -bool HiresTexture::LoadDDSTexture(Level& level, const std::string& filename) +bool HiresTexture::LoadDDSTexture(Level& level, const std::string& filename, u32 mip_level) { // Only loading a single mip level. File::IOFile file; @@ -479,6 +494,6 @@ bool HiresTexture::LoadDDSTexture(Level& level, const std::string& filename) if (!ParseDDSHeader(file, &info)) return false; - return ReadMipLevel(&level, file, info, info.width, info.height, info.first_mip_row_length, - info.first_mip_size); + return ReadMipLevel(&level, file, filename, mip_level, info, info.width, info.height, + info.first_mip_row_length, info.first_mip_size); }