From 2d97d0f108dd01889a463ac783c25bfa2ac45b9a Mon Sep 17 00:00:00 2001 From: Ribbit Date: Fri, 26 Sep 2025 23:07:52 -0700 Subject: [PATCH] [vk] Fix Validation Errors In Texture, Blit, and render passes --- src/video_core/renderer_vulkan/blit_image.cpp | 24 ++++--- .../renderer_vulkan/pipeline_helper.h | 6 +- .../renderer_vulkan/present/util.cpp | 2 +- .../renderer_vulkan/vk_buffer_cache.cpp | 16 +++++ .../renderer_vulkan/vk_compute_pass.cpp | 38 ++++++++--- .../renderer_vulkan/vk_graphics_pipeline.cpp | 61 +++++++++-------- .../renderer_vulkan/vk_render_pass_cache.cpp | 8 ++- .../renderer_vulkan/vk_render_pass_cache.h | 3 + .../renderer_vulkan/vk_texture_cache.cpp | 65 ++++++++++++++++--- .../renderer_vulkan/vk_texture_cache.h | 10 +++ 10 files changed, 170 insertions(+), 63 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 7bfcd6503b..93a734ab88 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -275,11 +275,12 @@ constexpr std::array MakeStages( } void UpdateOneTextureDescriptorSet(const Device& device, VkDescriptorSet descriptor_set, - VkSampler sampler, VkImageView image_view) { + VkSampler sampler, VkImageView image_view, + VkImageLayout image_layout = VK_IMAGE_LAYOUT_GENERAL) { const VkDescriptorImageInfo image_info{ .sampler = sampler, .imageView = image_view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .imageLayout = image_layout, }; const VkWriteDescriptorSet write_descriptor_set{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, @@ -408,9 +409,9 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo .subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, - .levelCount = 1, + .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, - .layerCount = 1, + .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, @@ -512,7 +513,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView } void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view, - VkImage src_image, VkSampler src_sampler, + VkImage src_image, VkSampler sampler, const Region2D& dst_region, const Region2D& src_region, const Extent3D& src_size) { const BlitImagePipelineKey key{ @@ -522,12 +523,12 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView const VkPipelineLayout layout = *one_texture_pipeline_layout; const VkPipeline pipeline = FindOrEmplaceColorPipeline(key); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region, + scheduler.Record([this, dst_framebuffer, src_image_view, src_image, sampler, dst_region, src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); BeginRenderPass(cmdbuf, dst_framebuffer); const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); - UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view); + UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_image_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, nullptr); @@ -1005,7 +1006,7 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend if (pipeline) { return; } - VkShaderModule frag_shader = *convert_depth_to_float_frag; + VkShaderModule frag_shader = *convert_float_to_depth_frag; const std::array stages = MakeStages(*full_screen_vert, frag_shader); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); pipeline = device.GetLogical().CreateGraphicsPipeline({ @@ -1021,7 +1022,7 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, - .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, + .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = *one_texture_pipeline_layout, .renderPass = renderpass, @@ -1039,6 +1040,9 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren } const std::array stages = MakeStages(*full_screen_vert, *module); const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); + const VkPipelineColorBlendStateCreateInfo* const color_blend_state = + is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO + : &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO; pipeline = device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = nullptr, @@ -1052,7 +1056,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr, - .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, + .pColorBlendState = color_blend_state, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout, .renderPass = renderpass, diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 910e07a606..0b5d619237 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -193,8 +193,10 @@ 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()}; + const bool enable_compare = + sampler.DepthCompareEnabled() && image_view.SupportsDepthCompare(); + const VkSampler vk_sampler = + sampler.HandleForUsage(use_fallback_sampler, enable_compare); guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); rescaling.PushTexture(texture_cache.IsRescaling(image_view)); } diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 0b1a89eec0..4bcd5b61fd 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -65,7 +65,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, - .layerCount = 1, + .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 55565e3d79..faf389d88c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -667,6 +667,22 @@ vk::Buffer BufferCacheRuntime::CreateNullBuffer() { scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([buffer = *ret](vk::CommandBuffer cmdbuf) { cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0); + const VkBufferMemoryBarrier vis{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_INDIRECT_COMMAND_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = buffer, + .offset = 0, + .size = VK_WHOLE_SIZE, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, + 0, vis); }); return ret; diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 5938de6100..7bde2b6e1c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -437,7 +437,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, }; const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); @@ -448,7 +448,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); cmdbuf.Dispatch(1, 1, 1); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0, write_barrier); + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier); }); } @@ -497,11 +497,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_INDEX_READ_BIT | - VK_ACCESS_UNIFORM_READ_BIT | - VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT }; const QueriesPrefixScanPushConstants uniforms{ .min_accumulation_base = static_cast(min_accumulation_limit), @@ -519,7 +515,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); cmdbuf.Dispatch(1, 1, 1); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier); }); } @@ -701,8 +697,30 @@ void MSAACopyPass::CopyImage(Image& dst_image, Image& src_image, copy.extent.depth, }; - scheduler.Record([this, dst = dst_image.Handle(), msaa_pipeline, num_dispatches, - descriptor_data](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, src = src_image.Handle(), dst = dst_image.Handle(), msaa_pipeline, num_dispatches, + descriptor_data](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = src, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); + const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, msaa_pipeline); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 745389213e..12cd46ca4b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -132,20 +132,10 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state) { key.depth_format = PixelFormat::Invalid; } key.samples = MaxwellToVK::MsaaMode(state.msaa_mode); + key.color_attachment_count = static_cast(Maxwell::NumRenderTargets); return key; } -size_t NumAttachments(const FixedPipelineState& state) { - size_t num{}; - for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { - const auto format{static_cast(state.color_formats[index])}; - if (format != Tegra::RenderTargetFormat::NONE) { - num = index + 1; - } - } - return num; -} - template bool Passes(const std::array& modules, const std::array& stage_infos) { @@ -770,30 +760,45 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { LOG_WARNING(Render_Vulkan, "Depth bounds is enabled but not supported"); } static_vector cb_attachments; - const size_t num_attachments{NumAttachments(key.state)}; - for (size_t index = 0; index < num_attachments; ++index) { + for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { static constexpr std::array mask_table{ VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, VK_COLOR_COMPONENT_A_BIT, }; - const auto& blend{key.state.attachments[index]}; - const std::array mask{blend.Mask()}; - VkColorComponentFlags write_mask{}; - for (size_t i = 0; i < mask_table.size(); ++i) { - write_mask |= mask[i] ? mask_table[i] : 0; + VkPipelineColorBlendAttachmentState attachment{}; + const auto format{static_cast(key.state.color_formats[index])}; + if (format != Tegra::RenderTargetFormat::NONE) { + const auto& blend{key.state.attachments[index]}; + const std::array mask{blend.Mask()}; + VkColorComponentFlags write_mask{}; + for (size_t i = 0; i < mask_table.size(); ++i) { + write_mask |= mask[i] ? mask_table[i] : 0; + } + attachment = VkPipelineColorBlendAttachmentState{ + .blendEnable = blend.enable != 0, + .srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.SourceRGBFactor()), + .dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.DestRGBFactor()), + .colorBlendOp = MaxwellToVK::BlendEquation(blend.EquationRGB()), + .srcAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.SourceAlphaFactor()), + .dstAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.DestAlphaFactor()), + .alphaBlendOp = MaxwellToVK::BlendEquation(blend.EquationAlpha()), + .colorWriteMask = write_mask, + }; + } else { + attachment = VkPipelineColorBlendAttachmentState{ + .blendEnable = VK_FALSE, + .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, + .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = 0, + }; } - cb_attachments.push_back({ - .blendEnable = blend.enable != 0, - .srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.SourceRGBFactor()), - .dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.DestRGBFactor()), - .colorBlendOp = MaxwellToVK::BlendEquation(blend.EquationRGB()), - .srcAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.SourceAlphaFactor()), - .dstAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.DestAlphaFactor()), - .alphaBlendOp = MaxwellToVK::BlendEquation(blend.EquationAlpha()), - .colorWriteMask = write_mask, - }); + cb_attachments.push_back(attachment); } const VkPipelineColorBlendStateCreateInfo color_blend_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp index 80ff75e3b9..5aeb70d218 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -6,6 +6,7 @@ #include +#include #include #include "video_core/renderer_vulkan/maxwell_to_vk.h" @@ -77,9 +78,10 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { } boost::container::static_vector descriptions; std::array references{}; - u32 num_attachments{}; + const u32 total_color_slots = + std::min(key.color_attachment_count, static_cast(key.color_formats.size())); u32 num_colors{}; - for (size_t index = 0; index < key.color_formats.size(); ++index) { + for (u32 index = 0; index < total_color_slots; ++index) { const PixelFormat format{key.color_formats[index]}; const bool is_valid{format != PixelFormat::Invalid}; references[index] = VkAttachmentReference{ @@ -88,10 +90,10 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { }; if (is_valid) { descriptions.push_back(AttachmentDescription(*device, format, key.samples)); - num_attachments = static_cast(index + 1); ++num_colors; } } + const u32 num_attachments = total_color_slots; const bool has_depth{key.depth_format != PixelFormat::Invalid}; VkAttachmentReference depth_reference{}; if (key.depth_format != PixelFormat::Invalid) { diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h index 91ad4bf577..f13702565e 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -6,6 +6,7 @@ #include #include +#include "common/common_types.h" #include "video_core/surface.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -17,6 +18,7 @@ struct RenderPassKey { std::array color_formats; VideoCore::Surface::PixelFormat depth_format; VkSampleCountFlagBits samples; + u8 color_attachment_count{}; }; } // namespace Vulkan @@ -27,6 +29,7 @@ struct hash { [[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept { size_t value = static_cast(key.depth_format) << 48; value ^= static_cast(key.samples) << 52; + value ^= static_cast(key.color_attachment_count) << 56; for (size_t i = 0; i < key.color_formats.size(); ++i) { value ^= static_cast(key.color_formats[i]) << (i * 6); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8d1d609a35..719790ad9d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2026,15 +2026,27 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI } } const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format); - if (ImageUsageFlags(format_info, format) != image.UsageFlags()) { + VkImageUsageFlags view_usage = ImageUsageFlags(format_info, format); + const VkImageUsageFlags image_usage = image.UsageFlags(); + const VkImageUsageFlags original_view_usage = view_usage; + const VkImageUsageFlags unsupported_usage = view_usage & ~image_usage; + if (unsupported_usage != 0) { LOG_WARNING(Render_Vulkan, - "Image view format {} has different usage flags than image format {}", format, - image.info.format); + "Clamping image view usage 0x{:X} for format {} to match image format {} (0x{:X})", + static_cast(original_view_usage), format, image.info.format, + static_cast(image_usage)); + view_usage &= image_usage; + } + if (view_usage == 0) { + LOG_WARNING(Render_Vulkan, + "Image view usage for format {} became 0 after clamping; falling back to image usage 0x{:X}", + format, static_cast(image_usage)); + view_usage = image_usage; } const VkImageViewUsageCreateInfo image_view_usage{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, - .usage = ImageUsageFlags(format_info, format), + .usage = view_usage, }; const VkImageViewCreateInfo create_info{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -2188,6 +2200,11 @@ bool ImageView::IsRescaled() const noexcept { return src_image.IsRescaled(); } +bool ImageView::SupportsDepthCompare() const noexcept { + const auto surface_type = VideoCore::Surface::GetFormatType(format); + return surface_type == SurfaceType::Depth || surface_type == SurfaceType::DepthStencil; +} + vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) { return device->GetLogical().CreateImageView({ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -2235,7 +2252,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_enabled) { return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = pnext, @@ -2249,7 +2266,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_enabled ? 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(), @@ -2259,12 +2276,41 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t }); }; - sampler = create_sampler(max_anisotropy); + depth_compare_enabled = tsc.depth_compare_enabled != 0; + sampler = create_sampler(max_anisotropy, depth_compare_enabled); 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); + const bool needs_default_anisotropy = max_anisotropy > max_anisotropy_default; + if (needs_default_anisotropy) { + sampler_default_anisotropy = create_sampler(max_anisotropy_default, depth_compare_enabled); } + if (depth_compare_enabled) { + sampler_no_compare = create_sampler(max_anisotropy, false); + if (needs_default_anisotropy) { + sampler_no_compare_default_anisotropy = create_sampler(max_anisotropy_default, false); + } + } +} + +VkSampler Sampler::HandleForUsage(bool use_default_anisotropy, bool enable_compare) const noexcept { + const bool wants_default = use_default_anisotropy && HasAddedAnisotropy(); + if (enable_compare || !depth_compare_enabled) { + if (wants_default && sampler_default_anisotropy) { + return *sampler_default_anisotropy; + } + return *sampler; + } + if (wants_default) { + if (sampler_no_compare_default_anisotropy) { + return *sampler_no_compare_default_anisotropy; + } + if (sampler_default_anisotropy) { + return *sampler_default_anisotropy; + } + } else if (sampler_no_compare) { + return *sampler_no_compare; + } + return wants_default && sampler_default_anisotropy ? *sampler_default_anisotropy : *sampler; } Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers, @@ -2293,6 +2339,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, ImageView* depth_buffer, bool is_rescaled_) { boost::container::small_vector attachments; RenderPassKey renderpass_key{}; + renderpass_key.color_attachment_count = static_cast(NUM_RT); s32 num_layers = 1; is_rescaled = is_rescaled_; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index cd11cc8fc7..d4b0547d36 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -237,6 +237,7 @@ public: Shader::ImageFormat image_format); [[nodiscard]] bool IsRescaled() const noexcept; + [[nodiscard]] bool SupportsDepthCompare() const noexcept; [[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept { return *image_views[static_cast(texture_type)]; @@ -303,9 +304,18 @@ public: return static_cast(sampler_default_anisotropy); } + [[nodiscard]] bool DepthCompareEnabled() const noexcept { + return depth_compare_enabled; + } + + [[nodiscard]] VkSampler HandleForUsage(bool use_default_anisotropy, bool enable_compare) const noexcept; + private: vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; + vk::Sampler sampler_no_compare; + vk::Sampler sampler_no_compare_default_anisotropy; + bool depth_compare_enabled = false; }; class Framebuffer {