From f45cd398efffc166f9d00fd9e260104c38ee69fb Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Thu, 3 May 2018 17:54:09 -0500 Subject: [PATCH] [Vulkan] Handle MIPs with non-power-of-two dimensions --- src/xenia/gpu/texture_info.cc | 34 +++++++++++++++++++-------- src/xenia/gpu/texture_info.h | 2 ++ src/xenia/gpu/vulkan/texture_cache.cc | 7 +++--- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/xenia/gpu/texture_info.cc b/src/xenia/gpu/texture_info.cc index 9c93a7872..abcdc8163 100644 --- a/src/xenia/gpu/texture_info.cc +++ b/src/xenia/gpu/texture_info.cc @@ -337,12 +337,6 @@ uint32_t TextureInfo::GetMipLocation(const TextureInfo& src, uint32_t mip, return src.guest_address; } - // One tile is 32x32 blocks - const uint32_t tile_size = src.format_info()->block_width * - src.format_info()->block_height * 32 * 32 * - src.format_info()->bits_per_pixel / 8; - - // Walk forward to find the address of the mip // If the texture is <= 16 pixels w/h, the mips are packed with the base // texture. Otherwise, they're stored beginning from mip_address. uint32_t address_base = std::min(src.width, src.height) < 16 @@ -350,17 +344,22 @@ uint32_t TextureInfo::GetMipLocation(const TextureInfo& src, uint32_t mip, : src.mip_address; uint32_t address_offset = 0; - uint32_t logical_width = std::max((src.width + 1) >> mip, 1u); - uint32_t logical_height = std::max((src.height + 1) >> mip, 1u); + // Walk forward to find the address of the mip. for (uint32_t i = 1; i < mip; i++) { - if (std::min(logical_width, logical_height) < 16) { + uint32_t logical_width = std::max(xe::next_pow2(src.width + 1) >> i, 1u); + uint32_t logical_height = std::max(xe::next_pow2(src.height + 1) >> i, 1u); + if (std::min(logical_width, logical_height) <= 16) { // We've reached the point where the mips are packed into a single tile. break; } - address_offset += std::max(src.input_length >> (i * 2), tile_size); + address_offset += GetMipSize(src, i); } + // Now, check if the mip is packed at an offset. + uint32_t logical_width = std::max(xe::next_pow2(src.width + 1) >> mip, 1u); + uint32_t logical_height = std::max(xe::next_pow2(src.height + 1) >> mip, 1u); + *offset_x = 0; *offset_y = 0; if (std::min(logical_width, logical_height) <= 16) { @@ -381,6 +380,21 @@ uint32_t TextureInfo::GetMipLocation(const TextureInfo& src, uint32_t mip, return address_base + address_offset; } +uint32_t TextureInfo::GetMipSize(const TextureInfo& src, uint32_t mip) { + uint32_t size = src.format_info()->block_width * + src.format_info()->block_height * + (xe::next_pow2(src.width + 1) >> mip) * + (xe::next_pow2(src.height + 1) >> mip) * + src.format_info()->bits_per_pixel / 8; + + // One tile is 32x32 blocks + uint32_t tile_size = src.format_info()->block_width * + src.format_info()->block_height * 32 * 32 * + src.format_info()->bits_per_pixel / 8; + + return std::max(size, tile_size); +} + uint32_t TextureInfo::GetMipLinearSize(const TextureInfo& src, uint32_t mip) { uint32_t bytes_per_block = src.format_info()->block_width * src.format_info()->block_height * diff --git a/src/xenia/gpu/texture_info.h b/src/xenia/gpu/texture_info.h index 930eff292..6b7bbfecb 100644 --- a/src/xenia/gpu/texture_info.h +++ b/src/xenia/gpu/texture_info.h @@ -318,6 +318,8 @@ struct TextureInfo { // Get the memory location of a mip. offset_x and offset_y are in blocks. static uint32_t GetMipLocation(const TextureInfo& src, uint32_t mip, uint32_t* offset_x, uint32_t* offset_y); + static uint32_t GetMipSize(const TextureInfo& src, uint32_t mip); + // Get the byte size of a MIP when stored linearly. static uint32_t GetMipLinearSize(const TextureInfo& src, uint32_t mip); diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index eac942b2e..6a602d287 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -888,12 +888,11 @@ bool TextureCache::ConvertTexture2D(uint8_t* dest, uint32_t input_width = src.size_2d.input_width >> mip; uint32_t input_height = src.size_2d.input_height >> mip; - // All dimensions must be at least one block w/h + // All dimensions must be a multiple of block w/h logical_width = std::max(logical_width, src.format_info()->block_width); logical_height = std::max(logical_height, src.format_info()->block_height); - block_width = std::max(block_width, src.format_info()->block_width); - input_width = std::max(input_width, src.format_info()->block_width); - input_height = std::max(input_height, src.format_info()->block_height); + input_width = xe::round_up(input_width, src.format_info()->block_width); + input_height = xe::round_up(input_height, src.format_info()->block_height); if (!src.is_tiled) { uint32_t offset_x, offset_y;