texture_cache: Handle image overlap when guest_size differs with same resource levels

Fixes a crash (UNREACHABLE) in ResolveOverlap when the guest requests
an image at the same address with the same format, tile mode, and
dimensions, but with a different array layer count that results in a
larger guest_size while having fewer or equal mip levels.

When the new image has a larger guest_size, the cached image is now
unconditionally freed since it is definitively superseded. When the
cached image is larger or equal, it is reused as a compatible view
source.

Signed-off-by: psabhishek26 <sploitpay.aosp.test@gmail.com>
This commit is contained in:
psabhishek26 2026-03-17 12:40:29 +05:30
parent 30ff9cf050
commit 945364562c

View File

@ -346,112 +346,49 @@ std::tuple<ImageId, int, int> TextureCache::ResolveOverlap(const ImageInfo& imag
return {merged_image_id, -1, -1};
}
// Enhanced debug logging for unreachable case
// Calculate expected size based on format and dimensions
u64 expected_size =
(static_cast<u64>(image_info.size.width) * static_cast<u64>(image_info.size.height) *
static_cast<u64>(image_info.size.depth) * static_cast<u64>(image_info.num_bits) / 8);
LOG_ERROR(Render_Vulkan,
"Unresolvable image overlap with equal memory address:\n"
"=== OLD IMAGE (cached) ===\n"
" Address: {:#x}\n"
" Size: {:#x} bytes\n"
" Format: {}\n"
" Type: {}\n"
" Width: {}\n"
" Height: {}\n"
" Depth: {}\n"
" Pitch: {}\n"
" Mip levels: {}\n"
" Array layers: {}\n"
" Samples: {}\n"
" Tile mode: {:#x}\n"
" Block size: {} bits\n"
" Is block-comp: {}\n"
" Guest size: {:#x}\n"
" Last accessed: tick {}\n"
" Safe to delete: {}\n"
"\n"
"=== NEW IMAGE (requested) ===\n"
" Address: {:#x}\n"
" Size: {:#x} bytes\n"
" Format: {}\n"
" Type: {}\n"
" Width: {}\n"
" Height: {}\n"
" Depth: {}\n"
" Pitch: {}\n"
" Mip levels: {}\n"
" Array layers: {}\n"
" Samples: {}\n"
" Tile mode: {:#x}\n"
" Block size: {} bits\n"
" Is block-comp: {}\n"
" Guest size: {:#x}\n"
"\n"
"=== COMPARISON ===\n"
" Same format: {}\n"
" Same type: {}\n"
" Same tile mode: {}\n"
" Same block size: {}\n"
" Same BlockDim: {}\n"
" Same pitch: {}\n"
" Old resources <= new: {} (old: {}, new: {})\n"
" Old size <= new size: {}\n"
" Expected size (calc): {} bytes\n"
" Size ratio (new/expected): {:.2f}x\n"
" Size ratio (new/old): {:.2f}x\n"
" Old vs expected diff: {} bytes ({:+.2f}%)\n"
" New vs expected diff: {} bytes ({:+.2f}%)\n"
" Merged image ID: {}\n"
" Binding type: {}\n"
" Current tick: {}\n"
" Age (ticks since last access): {}",
// Size is greater with same format and tile mode, but mip levels are not greater.
// This can occur when the guest reuses an address with a different array layer count
// or memory layout, causing a different memory footprint despite identical dimensions.
// The cached image is superseded and must be replaced.
if (image_info.guest_size > cache_image.info.guest_size) {
LOG_DEBUG(Render_Vulkan,
"Image overlap at {:#x}: replacing cached image "
"(size={:#x}, fmt={}, type={}, {}x{}x{}, {} layers, {} mips, tile={:#x}) "
"with new image "
"(size={:#x}, fmt={}, type={}, {}x{}x{}, {} layers, {} mips, tile={:#x})",
image_info.guest_address,
// Old image details
cache_image.info.guest_address, cache_image.info.guest_size,
vk::to_string(cache_image.info.pixel_format),
static_cast<int>(cache_image.info.type), cache_image.info.size.width,
cache_image.info.size.height, cache_image.info.size.depth, cache_image.info.pitch,
cache_image.info.resources.levels, cache_image.info.resources.layers,
cache_image.info.num_samples, static_cast<u32>(cache_image.info.tile_mode),
cache_image.info.num_bits, cache_image.info.props.is_block,
cache_image.info.guest_size, cache_image.tick_accessed_last, safe_to_delete,
// Cached image
cache_image.info.guest_size, vk::to_string(cache_image.info.pixel_format),
static_cast<int>(cache_image.info.type), cache_image.info.size.width,
cache_image.info.size.height, cache_image.info.size.depth,
cache_image.info.resources.layers, cache_image.info.resources.levels,
static_cast<u32>(cache_image.info.tile_mode),
// New image details
image_info.guest_address, image_info.guest_size,
vk::to_string(image_info.pixel_format), static_cast<int>(image_info.type),
image_info.size.width, image_info.size.height, image_info.size.depth,
image_info.pitch, image_info.resources.levels, image_info.resources.layers,
image_info.num_samples, static_cast<u32>(image_info.tile_mode),
image_info.num_bits, image_info.props.is_block, image_info.guest_size,
// New image
image_info.guest_size, vk::to_string(image_info.pixel_format),
static_cast<int>(image_info.type), image_info.size.width,
image_info.size.height, image_info.size.depth, image_info.resources.layers,
image_info.resources.levels, static_cast<u32>(image_info.tile_mode));
FreeImage(cache_image_id);
return {merged_image_id, -1, -1};
}
// Comparison
(image_info.pixel_format == cache_image.info.pixel_format),
(image_info.type == cache_image.info.type),
(image_info.tile_mode == cache_image.info.tile_mode),
(image_info.num_bits == cache_image.info.num_bits),
(image_info.BlockDim() == cache_image.info.BlockDim()),
(image_info.pitch == cache_image.info.pitch),
(cache_image.info.resources <= image_info.resources),
cache_image.info.resources.levels, image_info.resources.levels,
(cache_image.info.guest_size <= image_info.guest_size), expected_size,
// Size ratios
static_cast<double>(image_info.guest_size) / expected_size,
static_cast<double>(image_info.guest_size) / cache_image.info.guest_size,
// Difference between actual and expected sizes with percentages
static_cast<s64>(cache_image.info.guest_size) - static_cast<s64>(expected_size),
(static_cast<double>(cache_image.info.guest_size) / expected_size - 1.0) * 100.0,
static_cast<s64>(image_info.guest_size) - static_cast<s64>(expected_size),
(static_cast<double>(image_info.guest_size) / expected_size - 1.0) * 100.0,
merged_image_id.index, static_cast<int>(binding), scheduler.CurrentTick(),
scheduler.CurrentTick() - cache_image.tick_accessed_last);
UNREACHABLE_MSG("Encountered unresolvable image overlap with equal memory address.");
// The cached image is larger or equal with same format and tile mode.
// Treat it as a compatible view source.
LOG_DEBUG(Render_Vulkan,
"Image overlap at {:#x}: using cached image as view source "
"(cached size={:#x}, {} layers vs requested size={:#x}, {} layers)",
image_info.guest_address, cache_image.info.guest_size,
cache_image.info.resources.layers, image_info.guest_size,
image_info.resources.layers);
{
auto result_id = merged_image_id ? merged_image_id : cache_image_id;
const auto& result_image = slot_images[result_id];
const bool is_compatible =
IsVulkanFormatCompatible(result_image.info.pixel_format, image_info.pixel_format);
return {is_compatible ? result_id : ImageId{}, -1, -1};
}
}
// Right overlap, the image requested is a possible subresource of the image from cache.