Honor PS5 texture minimum LOD for streamed mip chains

Fix broken ground textures in ASTRO BOT.

Related work: https://github.com/KytyPS5/KytyPS5/pull/558

Co-authored-by: Çağatay Güçlü <33038942+defektu@users.noreply.github.com>
This commit is contained in:
nmzik
2026-09-16 20:58:13 +04:00
parent eda6ba37ec
commit f38d738ee2
5 changed files with 149 additions and 8 deletions
@@ -159,6 +159,7 @@ struct ImageViewInfo {
vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor;
uint32_t base_level = 0;
uint32_t level_count = 1;
uint32_t min_lod = 0; // U4.8 clamp relative to base_level.
uint32_t base_layer = 0;
uint32_t layer_count = 1;
vk::ComponentMapping mapping = {};
@@ -167,9 +168,10 @@ struct ImageViewInfo {
[[nodiscard]] bool operator==(const ImageViewInfo& rhs) const noexcept {
return format == rhs.format && type == rhs.type && aspect == rhs.aspect &&
base_level == rhs.base_level && level_count == rhs.level_count &&
base_layer == rhs.base_layer && layer_count == rhs.layer_count &&
mapping.r == rhs.mapping.r && mapping.g == rhs.mapping.g &&
mapping.b == rhs.mapping.b && mapping.a == rhs.mapping.a && usage == rhs.usage;
min_lod == rhs.min_lod && base_layer == rhs.base_layer &&
layer_count == rhs.layer_count && mapping.r == rhs.mapping.r &&
mapping.g == rhs.mapping.g && mapping.b == rhs.mapping.b &&
mapping.a == rhs.mapping.a && usage == rhs.usage;
}
};
@@ -362,6 +362,12 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
if (!is_storage) {
usage.usage &= ~vk::ImageUsageFlagBits::eStorage;
}
vk::ImageViewMinLodCreateInfoEXT min_lod {};
if (normalized.min_lod != 0) {
min_lod.minLod = static_cast<float>(normalized.base_level) +
static_cast<float>(normalized.min_lod) / 256.0f;
usage.pNext = &min_lod;
}
vk::ImageViewCreateInfo create {};
create.pNext = &usage;
create.image = image.image;
@@ -452,6 +452,14 @@ static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource&
view.aspect = vk::ImageAspectFlagBits::eColor;
view.base_level = descriptor.BaseLevel();
view.level_count = view_levels;
if (descriptor.MinLod() > descriptor.LastLevel() * 256u) {
EXIT("texture minimum LOD exceeds last mip level: min_lod=%u last_level=%u\n",
descriptor.MinLod(), descriptor.LastLevel());
}
const auto base_lod = view.base_level * 256u;
if (descriptor.MinLod() > base_lod) {
view.min_lod = descriptor.MinLod() - base_lod;
}
view.usage = storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlagBits::eSampled;
view.mapping =
storage || surface_format.conversion_format != Prospero::BufferFormat::kInvalid
@@ -201,6 +201,8 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
vk::PhysicalDeviceVulkan13Features features13 {};
vk::PhysicalDeviceColorWriteEnableFeaturesEXT color_write_ext {};
vk::PhysicalDeviceImageViewMinLodFeaturesEXT image_view_min_lod {};
color_write_ext.pNext = &image_view_min_lod;
vk::PhysicalDeviceDepthClipEnableFeaturesEXT depth_clip_enable {};
depth_clip_enable.pNext = &color_write_ext;
@@ -235,6 +237,10 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
skip_device = true;
#endif
}
if (image_view_min_lod.minLod != VK_TRUE) {
LOGF("image view minLod is not supported\n");
skip_device = true;
}
if (depth_clip_control.depthClipControl != VK_TRUE) {
LOGF("depthClipControl is not supported\n");
@@ -514,11 +520,14 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
depth_clip_enable.depthClipEnable = VK_TRUE;
vk::PhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control {};
vk::PhysicalDeviceImageViewMinLodFeaturesEXT image_view_min_lod {};
image_view_min_lod.minLod = VK_TRUE;
depth_clip_control.pNext = &image_view_min_lod;
// MoltenVK lacks VK_EXT_depth_clip_enable and VK_EXT_color_write_enable, so drop those
// feature structs from the chain on macOS (the renderer falls back to default depth
// clipping and static color-write masks).
#if !defined(__APPLE__)
depth_clip_control.pNext = &depth_clip_enable;
image_view_min_lod.pNext = &depth_clip_enable;
#endif
depth_clip_control.depthClipControl = VK_TRUE;
@@ -984,7 +993,8 @@ void WindowContext::CreateVulkan() {
std::vector<const char*> device_extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME,
VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, "VK_KHR_maintenance1"};
VK_EXT_IMAGE_VIEW_MIN_LOD_EXTENSION_NAME, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
"VK_KHR_maintenance1"};
#if defined(__APPLE__)
// MoltenVK lacks VK_EXT_depth_clip_enable and VK_EXT_color_write_enable; the renderer