From 88fcb7d338ae09e5b7dd4fa5f9fe2c055619c936 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 30 Jul 2026 15:13:04 -0400 Subject: [PATCH] [TEST] Adjustments on storage view + general image layouts --- .../renderer_vulkan/pipeline_helper.h | 7 +- .../renderer_vulkan/vk_query_cache.cpp | 3 +- .../renderer_vulkan/vk_texture_cache.cpp | 146 ++++++++++++++---- .../renderer_vulkan/vk_texture_cache.h | 6 +- 4 files changed, 125 insertions(+), 37 deletions(-) diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index d084c3b99b..1350662ba9 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -260,7 +260,12 @@ inline void PushImageDescriptors(TextureCache& texture_cache, if (desc.is_written) { texture_cache.MarkModification(image_view.image_id); } - const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)}; + VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)}; + if (vk_image_view == VK_NULL_HANDLE) { + const VkImageView null_image_view{ + texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)}; + if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view; + } guest_descriptor_queue.AddImage(vk_image_view); const bool element_rescaled{texture_cache.IsRescaling(image_view)}; is_rescaled |= element_rescaled; diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 55488fb724..10fead078b 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -1540,7 +1540,8 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku const auto driver_id = impl->device.GetDriverID(); const bool is_gpu_high = Settings::IsGPULevelHigh(); - if (!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { + if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || + driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { EndHostConditionalRendering(); return true; } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index b25ff6b1b1..2af5fd42df 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -57,7 +57,7 @@ constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; constexpr bool ENABLE_MSAA_DEPTH_DISCARD = true; constexpr bool ENABLE_MSAA_DEPTH_RESOLVE = true; -constexpr bool ENABLE_OPTIMAL_IMAGE_LAYOUTS = true; +constexpr bool ENABLE_OPTIMAL_IMAGE_LAYOUTS = false; constexpr VkBorderColor ConvertBorderColor(const std::array& color) { if (color == std::array{0, 0, 0, 0}) { @@ -158,6 +158,74 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { return WillUseAcceleratedAstcDecode(device, info) || WillUseComputeUnswizzle(info); } +[[nodiscard]] bool StorageAllowedForSamples(const Device& device, VkSampleCountFlagBits samples) { + if (samples == VK_SAMPLE_COUNT_1_BIT) { + return true; + } + return device.IsStorageImageMultisampleSupported() && + (device.GetStorageImageSampleCounts() & static_cast(samples)) != 0; +} + +[[nodiscard]] bool HasStorageCompatibleView(const Device& device, + std::span view_formats) { + return std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) { + return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, + FormatType::Optimal); + }); +} + +[[nodiscard]] boost::container::small_vector ShaderStorageViewFormats( + const Device& device, const ImageInfo& info) { + using VideoCore::Surface::BytesPerBlock; + using VideoCore::Surface::DefaultBlockHeight; + using VideoCore::Surface::DefaultBlockWidth; + using VideoCore::Surface::GetFormatType; + using VideoCore::Surface::SurfaceType; + + boost::container::small_vector formats; + if (GetFormatType(info.format) != SurfaceType::ColorTexture) { + return formats; + } + if (DefaultBlockWidth(info.format) != 1 || DefaultBlockHeight(info.format) != 1) { + return formats; + } + static constexpr std::array, 7> CANDIDATES{{ + {1, VK_FORMAT_R8_UINT}, + {1, VK_FORMAT_R8_SINT}, + {2, VK_FORMAT_R16_UINT}, + {2, VK_FORMAT_R16_SINT}, + {4, VK_FORMAT_R32_UINT}, + {8, VK_FORMAT_R32G32_UINT}, + {16, VK_FORMAT_R32G32B32A32_UINT}, + }}; + const u32 bytes_per_texel = BytesPerBlock(info.format); + for (const auto& [size, view_format] : CANDIDATES) { + if (size != bytes_per_texel) { + continue; + } + if (!device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, + FormatType::Optimal)) { + continue; + } + formats.push_back(view_format); + } + return formats; +} + +[[nodiscard]] bool CanUseStorage(const Device& device, const ImageInfo& info, + std::span view_formats) { + if (info.type == ImageType::Buffer) { + return false; + } + if (!StorageAllowedForSamples(device, ConvertSampleCount(info.num_samples))) { + return false; + } + if (MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format).storage) { + return true; + } + return view_formats.size() > 1 && HasStorageCompatibleView(device, view_formats); +} + [[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info, std::optional format_override = {}, bool want_storage = true) { @@ -179,7 +247,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples); const VkSampleCountFlagBits samples = ConvertSampleCount(info.num_samples); VkImageUsageFlags usage = ImageUsageFlags(format_info, info.format, want_storage); - if (samples != VK_SAMPLE_COUNT_1_BIT && !device.IsStorageImageMultisampleSupported()) { + if (!StorageAllowedForSamples(device, samples)) { usage &= ~static_cast(VK_IMAGE_USAGE_STORAGE_BIT); } return VkImageCreateInfo{ @@ -214,37 +282,39 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { } VkImageCreateInfo image_ci = MakeImageCreateInfo(device, info, format_override, want_storage); + const bool grants_storage = + want_storage && StorageAllowedForSamples(device, image_ci.samples) && + ((image_ci.usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0 || + (view_formats.size() > 1 && HasStorageCompatibleView(device, view_formats))); + + boost::container::small_vector formats(view_formats.begin(), view_formats.end()); + if (grants_storage) { + const auto storage_formats = ShaderStorageViewFormats(device, info); + if (!storage_formats.empty()) { + if (std::find(formats.begin(), formats.end(), image_ci.format) == formats.end()) { + formats.push_back(image_ci.format); + } + for (const VkFormat storage_format : storage_formats) { + if (std::find(formats.begin(), formats.end(), storage_format) == formats.end()) { + formats.push_back(storage_format); + } + } + } + if (!device.IsFormatSupported(image_ci.format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, + FormatType::Optimal)) { + image_ci.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; + } + image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + const VkImageFormatListCreateInfo image_format_list = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, .pNext = nullptr, - .viewFormatCount = static_cast(view_formats.size()), - .pViewFormats = view_formats.data(), + .viewFormatCount = static_cast(formats.size()), + .pViewFormats = formats.data(), }; - if (view_formats.size() > 1) { + if (formats.size() > 1) { image_ci.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; - - const bool has_storage_compatible_view = - std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) { - return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, - FormatType::Optimal); - }); - // storageImageSampleCounts is only meaningful once shaderStorageImageMultisample is on; - // that feature is what the spec actually gates multisampled storage usage on. - const bool storage_allowed_for_samples = - image_ci.samples == VK_SAMPLE_COUNT_1_BIT || - (device.IsStorageImageMultisampleSupported() && - (device.GetStorageImageSampleCounts() & - static_cast(image_ci.samples)) != 0); - if (want_storage && has_storage_compatible_view && storage_allowed_for_samples) { - // Requesting a usage the image's own format cannot support is the only thing here - // that needs extended usage; views narrow their own usage on creation. - if (!device.IsFormatSupported(image_ci.format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, - FormatType::Optimal)) { - image_ci.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; - } - image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT; - } - if (device.IsKhrImageFormatListSupported()) { image_ci.pNext = &image_format_list; } @@ -2009,9 +2079,8 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu VAddr cpu_addr_) : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler}, runtime{&runtime_}, - storage_capable(MaxwellToVK::SurfaceFormat(runtime_.device, FormatType::Optimal, false, - info_.format) - .storage), + storage_capable( + CanUseStorage(runtime_.device, info_, runtime_.ViewFormats(info_.format))), wants_storage(StorageNeededAtCreation(runtime_.device, info_)), original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info, runtime->ViewFormats(info.format), {}, wants_storage)), @@ -2464,7 +2533,9 @@ bool Image::EnableStorageUsage() { } vk::Image new_original = MakeImage(runtime->device, runtime->memory_allocator, info, view_formats, {}, true); - if (!new_original) { + if (!new_original || (new_original.UsageFlags() & VK_IMAGE_USAGE_STORAGE_BIT) == 0) { + LOG_WARNING(Render_Vulkan, "Cannot promote image to storage usage, format={}", info.format); + storage_promotion_failed = true; return false; } vk::Image new_scaled; @@ -2472,6 +2543,7 @@ bool Image::EnableStorageUsage() { new_scaled = MakeImage(runtime->device, runtime->memory_allocator, scaled_info, view_formats, {}, true); if (!new_scaled) { + storage_promotion_failed = true; return false; } } @@ -2815,6 +2887,9 @@ VkImageView ImageView::ColorView() { VkImageView ImageView::StorageView(Shader::TextureType texture_type, Shader::ImageFormat image_format) { + if ((image_usage & VK_IMAGE_USAGE_STORAGE_BIT) == 0) { + return VK_NULL_HANDLE; + } if (image_handle) { if (image_format == Shader::ImageFormat::Typeless) { auto& view{typeless_storage_views[static_cast(texture_type)]}; @@ -2870,7 +2945,12 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; const VkImageUsageFlags view_usage = ViewUsageFlags(*device, image_usage, vk_format); - const bool narrows = view_usage != image_usage && (view_usage & VIEW_MEANINGFUL_USAGE) != 0; + if ((view_usage & VIEW_MEANINGFUL_USAGE) == 0) { + LOG_WARNING(Render_Vulkan, "Unsupported view format={} over image usage={:#x}", + static_cast(vk_format), image_usage); + return vk::ImageView{}; + } + const bool narrows = view_usage != image_usage; const VkImageViewUsageCreateInfo usage_ci{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index c2997d2326..f8f94ada3f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -393,8 +393,9 @@ public: VkImageView StorageImageView(s32 level) noexcept; [[nodiscard]] bool NeedsStorageUsage() const noexcept { - return storage_capable && !wants_storage && runtime != nullptr && - current_image != nullptr && static_cast(this->*current_image); + return storage_capable && !wants_storage && !storage_promotion_failed && + runtime != nullptr && current_image != nullptr && + static_cast(this->*current_image); } bool EnableStorageUsage(); @@ -433,6 +434,7 @@ private: bool storage_capable = false; bool wants_storage = false; + bool storage_promotion_failed = false; bool attachment_used = false; VkImageLayout current_layout = VK_IMAGE_LAYOUT_GENERAL;