From 6b1e22632eb888914ff1c6deb4fe55b86f662b0f Mon Sep 17 00:00:00 2001 From: Ribbit Date: Sat, 27 Sep 2025 12:39:28 -0700 Subject: [PATCH] [vk] Even more validation fixes that require extensive testing --- .../backend/spirv/spirv_emit_context.cpp | 13 +++++ .../vulkan_blit_depth_stencil.frag | 4 +- .../renderer_vulkan/pipeline_helper.h | 4 +- .../renderer_vulkan/vk_swapchain.cpp | 47 +++++++++++++++---- .../renderer_vulkan/vk_texture_cache.cpp | 29 ++++++++++-- .../renderer_vulkan/vk_texture_cache.h | 4 +- .../vulkan_common/vulkan_device.cpp | 29 ++++++++++++ src/video_core/vulkan_common/vulkan_device.h | 3 ++ 8 files changed, 116 insertions(+), 17 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 4c3e101433..91674c9b5f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1432,6 +1432,9 @@ void EmitContext::DefineInputs(const IR::Program& program) { } if (info.uses_sample_id) { sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); + if (stage == Stage::Fragment) { + Decorate(sample_id, spv::Decoration::Flat); + } } if (info.uses_is_helper_invocation) { is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); @@ -1442,6 +1445,13 @@ void EmitContext::DefineInputs(const IR::Program& program) { subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR); subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR); subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR); + if (stage == Stage::Fragment) { + Decorate(subgroup_mask_eq, spv::Decoration::Flat); + Decorate(subgroup_mask_lt, spv::Decoration::Flat); + Decorate(subgroup_mask_le, spv::Decoration::Flat); + Decorate(subgroup_mask_gt, spv::Decoration::Flat); + Decorate(subgroup_mask_ge, spv::Decoration::Flat); + } } if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || (profile.warp_size_potentially_larger_than_guest && @@ -1461,6 +1471,9 @@ void EmitContext::DefineInputs(const IR::Program& program) { } if (loads[IR::Attribute::PrimitiveId]) { primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId); + if (stage == Stage::Fragment) { + Decorate(primitive_id, spv::Decoration::Flat); + } } if (loads[IR::Attribute::Layer]) { AddCapability(spv::Capability::Geometry); diff --git a/src/video_core/host_shaders/vulkan_blit_depth_stencil.frag b/src/video_core/host_shaders/vulkan_blit_depth_stencil.frag index 484b99773c..f4f579c02e 100644 --- a/src/video_core/host_shaders/vulkan_blit_depth_stencil.frag +++ b/src/video_core/host_shaders/vulkan_blit_depth_stencil.frag @@ -5,11 +5,11 @@ #extension GL_ARB_shader_stencil_export : require layout(binding = 0) uniform sampler2D depth_tex; -layout(binding = 1) uniform isampler2D stencil_tex; +layout(binding = 1) uniform usampler2D stencil_tex; layout(location = 0) in vec2 texcoord; void main() { gl_FragDepth = textureLod(depth_tex, texcoord, 0).r; - gl_FragStencilRefARB = textureLod(stencil_tex, texcoord, 0).r; + gl_FragStencilRefARB = int(textureLod(stencil_tex, texcoord, 0).r); } diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 0b5d619237..ddefb67441 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -193,8 +193,8 @@ 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 bool enable_compare = - sampler.DepthCompareEnabled() && image_view.SupportsDepthCompare(); + const bool enable_compare = sampler.DepthCompareEnabled() && + image_view.SupportsDepthCompare(desc.type); const VkSampler vk_sampler = sampler.HandleForUsage(use_fallback_sampler, enable_compare); guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 3b35e28c05..d7c3af7a11 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -306,14 +306,48 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) { swapchain_ci.queueFamilyIndexCount = static_cast(queue_indices.size()); swapchain_ci.pQueueFamilyIndices = queue_indices.data(); } - static constexpr std::array view_formats{VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB}; + const VkFormat swapchain_format = surface_format.format; + const bool has_mutable_swapchain = device.IsKhrSwapchainMutableFormatEnabled(); +#ifdef ANDROID + // Android is already ordered the same as Switch. + VkFormat preferred_view_format = VK_FORMAT_R8G8B8A8_UNORM; +#else + VkFormat preferred_view_format = VK_FORMAT_B8G8R8A8_UNORM; +#endif + if (!has_mutable_swapchain) { + preferred_view_format = swapchain_format; + } + std::array view_formats{}; + u32 view_format_count = 0; + const auto push_format = [&](VkFormat format) { + if (format == VK_FORMAT_UNDEFINED || view_format_count >= static_cast(view_formats.size())) { + return; + } + const auto end_iterator = view_formats.begin() + view_format_count; + if (std::find(view_formats.begin(), end_iterator, format) != end_iterator) { + return; + } + view_formats[view_format_count++] = format; + }; + push_format(swapchain_format); + if (has_mutable_swapchain) { + push_format(preferred_view_format); + if (swapchain_format == VK_FORMAT_B8G8R8A8_UNORM || + preferred_view_format == VK_FORMAT_B8G8R8A8_UNORM) { + push_format(VK_FORMAT_B8G8R8A8_SRGB); + } + if (swapchain_format == VK_FORMAT_R8G8B8A8_UNORM || + preferred_view_format == VK_FORMAT_R8G8B8A8_UNORM) { + push_format(VK_FORMAT_R8G8B8A8_SRGB); + } + } VkImageFormatListCreateInfo format_list{ .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, .pNext = nullptr, - .viewFormatCount = static_cast(view_formats.size()), + .viewFormatCount = view_format_count, .pViewFormats = view_formats.data(), }; - if (device.IsKhrSwapchainMutableFormatEnabled()) { + if (has_mutable_swapchain && view_format_count > 1) { format_list.pNext = std::exchange(swapchain_ci.pNext, &format_list); swapchain_ci.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR; } @@ -331,12 +365,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) { images = swapchain.GetImages(); image_count = static_cast(images.size()); -#ifdef ANDROID - // Android is already ordered the same as Switch. - image_view_format = VK_FORMAT_R8G8B8A8_UNORM; -#else - image_view_format = VK_FORMAT_B8G8R8A8_UNORM; -#endif + image_view_format = preferred_view_format; } void Swapchain::CreateSemaphores() { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 719790ad9d..80294faf66 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2010,6 +2010,8 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI samples(ConvertSampleCount(image.info.num_samples)) { using Shader::TextureType; + sampled_formats.fill(VK_FORMAT_UNDEFINED); + const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info); std::array swizzle{ SwizzleSource::R, @@ -2026,6 +2028,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI } } const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format); + view_format = format_info.format; VkImageUsageFlags view_usage = ImageUsageFlags(format_info, format); const VkImageUsageFlags image_usage = image.UsageFlags(); const VkImageUsageFlags original_view_usage = view_usage; @@ -2074,6 +2077,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI handle.SetObjectNameEXT(VideoCommon::Name(*this, gpu_addr).c_str()); } image_views[static_cast(tex_type)] = std::move(handle); + sampled_formats[static_cast(tex_type)] = ci.format; }; switch (info.type) { case VideoCommon::ImageViewType::e1D: @@ -2113,10 +2117,14 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info, const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_) : VideoCommon::ImageViewBase{info, view_info, gpu_addr_}, - buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} + buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} { + sampled_formats.fill(VK_FORMAT_UNDEFINED); + view_format = VK_FORMAT_UNDEFINED; +} ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params) : VideoCommon::ImageViewBase{params}, device{&runtime.device} { + sampled_formats.fill(VK_FORMAT_UNDEFINED); if (device->HasNullDescriptor()) { return; } @@ -2129,7 +2137,9 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageV image_handle = *null_image; for (u32 i = 0; i < Shader::NUM_TEXTURE_TYPES; i++) { image_views[i] = MakeView(VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_ASPECT_COLOR_BIT); + sampled_formats[i] = VK_FORMAT_A8B8G8R8_UNORM_PACK32; } + view_format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; } ImageView::~ImageView() = default; @@ -2200,9 +2210,22 @@ bool ImageView::IsRescaled() const noexcept { return src_image.IsRescaled(); } -bool ImageView::SupportsDepthCompare() const noexcept { +bool ImageView::SupportsDepthCompare(Shader::TextureType texture_type) const noexcept { + if (device == nullptr || image_handle == VK_NULL_HANDLE) { + return false; + } const auto surface_type = VideoCore::Surface::GetFormatType(format); - return surface_type == SurfaceType::Depth || surface_type == SurfaceType::DepthStencil; + if (surface_type != SurfaceType::Depth && surface_type != SurfaceType::DepthStencil) { + return false; + } + const VkFormat sampled_format = sampled_formats[static_cast(texture_type)]; + if (sampled_format != VK_FORMAT_UNDEFINED) { + return device->SupportsDepthComparisonSampling(sampled_format); + } + if (view_format == VK_FORMAT_UNDEFINED) { + return false; + } + return device->SupportsDepthComparisonSampling(view_format); } vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index d4b0547d36..5bf72fc34c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -237,7 +237,7 @@ public: Shader::ImageFormat image_format); [[nodiscard]] bool IsRescaled() const noexcept; - [[nodiscard]] bool SupportsDepthCompare() const noexcept; + [[nodiscard]] bool SupportsDepthCompare(Shader::TextureType texture_type) const noexcept; [[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept { return *image_views[static_cast(texture_type)]; @@ -283,6 +283,8 @@ private: VkImage image_handle = VK_NULL_HANDLE; VkImageView render_target = VK_NULL_HANDLE; VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; + VkFormat view_format = VK_FORMAT_UNDEFINED; + std::array sampled_formats{}; u32 buffer_size = 0; }; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 0e0bec2ce3..e25afede82 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -886,6 +886,34 @@ bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags want return (supported_usage & wanted_usage) == wanted_usage; } +bool Device::SupportsDepthComparisonSampling(VkFormat format) const { + const auto it = format_properties.find(format); + if (it == format_properties.end()) { + UNIMPLEMENTED_MSG("Unimplemented depth comparison query format={}", format); + return false; + } +#if defined(VK_FORMAT_FEATURE_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) + const auto features = it->second.optimalTilingFeatures; + return (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0; +#elif defined(VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) + const auto get_props2 = reinterpret_cast( + dld.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2")); + if (!get_props2) { + const auto fallback_features = it->second.optimalTilingFeatures; + return (fallback_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0; + } + VkFormatProperties3 format_props3{VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3}; + VkFormatProperties2 format_props2{VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2}; + format_props2.pNext = &format_props3; + get_props2(physical, format, &format_props2); + return (format_props3.optimalTilingFeatures & + VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0; +#else + const auto fallback_features = it->second.optimalTilingFeatures; + return (fallback_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0; +#endif +} + std::string Device::GetDriverName() const { switch (properties.driver.driverID) { case VK_DRIVER_ID_AMD_PROPRIETARY: @@ -1448,3 +1476,4 @@ std::vector Device::GetDeviceQueueCreateInfos() const { } } // namespace Vulkan + diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index bd54144480..9849d338cf 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -213,6 +213,9 @@ public: bool IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, FormatType format_type) const; + /// Returns true when the format supports depth comparison sampling on optimal tiling images. + bool SupportsDepthComparisonSampling(VkFormat format) const; + /// Reports a device loss. void ReportLoss() const;