From 1ebb33f112c893c352faabc96356d63648cef711 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 7 Jun 2026 23:48:23 -0600 Subject: [PATCH] Validate texture replacement size before loading Fixes TwilitRealm/dusklight#1226 --- lib/gfx/texture.hpp | 1 + lib/gfx/texture_format.cpp | 9 +++++++++ lib/gfx/texture_replacement.cpp | 32 +++++++++++++++++++++++++++----- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/gfx/texture.hpp b/lib/gfx/texture.hpp index 14d89c9..ac7c91c 100644 --- a/lib/gfx/texture.hpp +++ b/lib/gfx/texture.hpp @@ -30,6 +30,7 @@ struct TextureFormatInfo { }; TextureFormatInfo format_info(wgpu::TextureFormat format) noexcept; uint64_t calc_texture_size(wgpu::TextureFormat format, uint32_t width, uint32_t height, uint32_t mips) noexcept; +bool is_block_aligned(wgpu::TextureFormat format, uint32_t width, uint32_t height) noexcept; constexpr u32 InvalidTextureFormat = -1; struct TextureRef { diff --git a/lib/gfx/texture_format.cpp b/lib/gfx/texture_format.cpp index 9eb520d..4ecec43 100644 --- a/lib/gfx/texture_format.cpp +++ b/lib/gfx/texture_format.cpp @@ -90,4 +90,13 @@ uint64_t calc_texture_size(wgpu::TextureFormat format, u32 width, u32 height, u3 } return total; } + +bool is_block_aligned(wgpu::TextureFormat format, uint32_t width, uint32_t height) noexcept { + if (width == 0 || height == 0) { + return false; + } + + const auto info = format_info(format); + return !info.compressed || (width % info.blockWidth == 0 && height % info.blockHeight == 0); +} } // namespace aurora::gfx diff --git a/lib/gfx/texture_replacement.cpp b/lib/gfx/texture_replacement.cpp index e742dfc..ad37737 100644 --- a/lib/gfx/texture_replacement.cpp +++ b/lib/gfx/texture_replacement.cpp @@ -469,6 +469,20 @@ constexpr bool is_unsupported_texture_format(wgpu::TextureFormat format) { } } +bool validate_texture_size(wgpu::TextureFormat format, uint32_t width, uint32_t height, + std::string_view label) noexcept { + if (aurora::gfx::is_block_aligned(format, width, height)) { + return true; + } + + const auto info = aurora::gfx::format_info(format); + Log.warn( + "texture_replacement: failed to load texture {} because {}x{} is not aligned to {}x{} texel blocks for " + "format {}", + label, width, height, info.blockWidth, info.blockHeight, static_cast(format)); + return false; +} + std::optional load_file_replacement(const ReplacementEntry& entry) noexcept { auto base = load_texture_file(entry.path); if (!base.has_value()) { @@ -480,6 +494,9 @@ std::optional load_file_replacement(const Replace fs_path_to_string(entry.path), static_cast(base->format)); return std::nullopt; } + if (!validate_texture_size(base->format, base->width, base->height, fs_path_to_string(entry.path))) { + return std::nullopt; + } if (base->mips > 1) { return base; @@ -598,16 +615,21 @@ aurora::gfx::TextureHandle create_raw_texture_handle(const ReplacementEntry& ent return {}; } + const auto label = entry.label.empty() ? fmt::format("{}", entry.id) : entry.label; const auto format = aurora::gfx::to_wgpu(entry.gxFormat); if (is_unsupported_texture_format(format)) { - Log.warn("texture_replacement: failed to load raw replacement {} due to unsupported format: {}", - entry.label.empty() ? fmt::format("{}", entry.id) : entry.label, static_cast(format)); + Log.warn("texture_replacement: failed to load raw replacement {} due to unsupported format: {}", label, + static_cast(format)); + return {}; + } + if (!validate_texture_size(format, entry.width, entry.height, label)) { return {}; } - const auto label = entry.label.empty() ? fmt::format("TextureReplacement {}", entry.id) : entry.label; - auto handle = aurora::gfx::new_static_texture_2d(entry.width, entry.height, entry.mipCount, entry.gxFormat, - {entry.bytes.data(), entry.bytes.size()}, false, label.c_str()); + const auto textureLabel = entry.label.empty() ? fmt::format("TextureReplacement {}", entry.id) : entry.label; + auto handle = + aurora::gfx::new_static_texture_2d(entry.width, entry.height, entry.mipCount, entry.gxFormat, + {entry.bytes.data(), entry.bytes.size()}, false, textureLabel.c_str()); if (handle) { handle->isReplacement = true; }