diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index e88b27b273..26541a0c1b 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -14,6 +14,7 @@ #include "shader_recompiler/backend/spirv/emit_spirv.h" #include "shader_recompiler/shader_info.h" #include "video_core/renderer_vulkan/vk_texture_cache.h" +#include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/texture_cache/types.h" #include "video_core/vulkan_common/vulkan_device.h" @@ -197,8 +198,29 @@ inline void PushImageDescriptors(TextureCache& texture_cache, const Sampler& sampler{texture_cache.GetSampler(sampler_id)}; const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && !image_view.SupportsAnisotropy()}; - const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() - : sampler.Handle()}; + + // In case sampler requires depth comparison but the format doesn't support it. + bool need_no_compare = false; + if (sampler.HasCompareEnabled()) { + const auto& device = texture_cache.runtime.device; + const auto fmt_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, + image_view.format); + if (!device.IsFormatSupported(fmt_info.format, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT, + FormatType::Optimal)) { + need_no_compare = true; + } + } + + VkSampler vk_sampler; + if (need_no_compare) { + vk_sampler = use_fallback_sampler ? + sampler.HandleNoCompareWithDefaultAnisotropy() + : sampler.HandleNoCompare(); + } else { + vk_sampler = use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() + : sampler.Handle(); + } guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); rescaling.PushTexture(texture_cache.IsRescaling(image_view)); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index f099db74cb..facf5e5515 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2284,8 +2284,28 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type, storage_views.emplace(); auto& views{is_signed ? storage_views->signeds : storage_views->unsigneds}; auto& view{views[size_t(texture_type)]}; - if (!view) - view = MakeView(Format(image_format), VK_IMAGE_ASPECT_COLOR_BIT); + if (!view) { + static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, + .pNext = nullptr, + .usage = VK_IMAGE_USAGE_STORAGE_BIT, + }; + view = device->GetLogical().CreateImageView({ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = &storage_image_view_usage_create_info, + .flags = 0, + .image = image_handle, + .viewType = ImageViewType(type), + .format = Format(image_format), + .components{ + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = MakeSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, range), + }); + } return *view; } return VK_NULL_HANDLE; @@ -2350,7 +2370,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t // Some games have samplers with garbage. Sanitize them here. const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); - const auto create_sampler = [&](const f32 anisotropy) { + const auto create_sampler = [&](const f32 anisotropy, bool compare_enable) { return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = pnext, @@ -2364,7 +2384,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .mipLodBias = tsc.LodBias(), .anisotropyEnable = static_cast(anisotropy > 1.0f ? VK_TRUE : VK_FALSE), .maxAnisotropy = anisotropy, - .compareEnable = tsc.depth_compare_enabled, + .compareEnable = compare_enable ? VK_TRUE : VK_FALSE, .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), @@ -2374,11 +2394,23 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t }); }; - sampler = create_sampler(max_anisotropy); + compare_enable = static_cast(tsc.depth_compare_enabled); + + sampler = create_sampler(max_anisotropy, compare_enable); + if (compare_enable) { + sampler_no_compare = create_sampler(max_anisotropy, false); + } else { + sampler_no_compare = sampler; + } const f32 max_anisotropy_default = static_cast(1U << tsc.max_anisotropy); if (max_anisotropy > max_anisotropy_default) { - sampler_default_anisotropy = create_sampler(max_anisotropy_default); + sampler_default_anisotropy = create_sampler(max_anisotropy_default, compare_enable); + if (compare_enable) { + sampler_no_compare_default_anisotropy = create_sampler(max_anisotropy_default, false); + } else { + sampler_no_compare_default_anisotropy = sampler_default_anisotropy; + } } } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 4bb9687ab0..266ed89279 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -408,9 +408,28 @@ public: return static_cast(sampler_default_anisotropy); } + [[nodiscard]] bool HasCompareEnabled() const noexcept { + return compare_enable; + } + + [[nodiscard]] VkSampler HandleNoCompare() const noexcept { + return sampler_no_compare ? *sampler_no_compare : *sampler; + } + + [[nodiscard]] VkSampler HandleNoCompareWithDefaultAnisotropy() const noexcept { + if (sampler_no_compare_default_anisotropy) + return *sampler_no_compare_default_anisotropy; + if (sampler_default_anisotropy) + return *sampler_default_anisotropy; + return *sampler; + } + private: vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; + vk::Sampler sampler_no_compare; + vk::Sampler sampler_no_compare_default_anisotropy; + bool compare_enable = false; }; struct TextureCacheParams { diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index ef02972209..4bc9053e7a 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -7,6 +7,7 @@ #include #include "common/assert.h" +#include "common/logging.h" #include "common/settings.h" #include "video_core/surface.h" #include "video_core/texture_cache/format_lookup_table.h" @@ -130,6 +131,23 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept { size.width *= NumSamplesX(config.msaa_mode); size.height *= NumSamplesY(config.msaa_mode); } + + { + u32 max_dim = std::max({size.width, size.height, size.depth}); + if (max_dim == 0) { + max_dim = 1; + } + u32 max_mip_levels = 1; + while (max_dim > 1) { + max_dim >>= 1; + ++max_mip_levels; + } + if (resources.levels > static_cast(max_mip_levels)) { + LOG_WARNING(HW_GPU, "Clamping mip levels from {} to {} for size {}x{}x{}", + resources.levels, max_mip_levels, size.width, size.height, size.depth); + resources.levels = static_cast(max_mip_levels); + } + } if (type != ImageType::Linear) { // FIXME: Call this without passing *this layer_stride = CalculateLayerStride(*this);