From f9e5ac191e16f0fc65462be61cf07c0fb0834365 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 12 Aug 2026 01:19:52 -0400 Subject: [PATCH] [TEST] Depth stencil resolve + MSAA depth stencil --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 2 + .../renderer_vulkan/vk_render_pass_cache.cpp | 173 +++++++++++++++++- .../renderer_vulkan/vk_render_pass_cache.h | 7 +- .../renderer_vulkan/vk_texture_cache.cpp | 117 ++++++++---- .../renderer_vulkan/vk_texture_cache.h | 5 +- .../vulkan_common/vulkan_device.cpp | 11 ++ src/video_core/vulkan_common/vulkan_device.h | 34 ++++ .../vulkan_common/vulkan_wrapper.cpp | 12 ++ src/video_core/vulkan_common/vulkan_wrapper.h | 3 + 9 files changed, 330 insertions(+), 34 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8b5b0bc9c5..345278ab41 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -140,6 +140,8 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state, const Device& d }); key.resolve_color = key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler(); + key.resolve_depth_stencil = key.samples != VK_SAMPLE_COUNT_1_BIT && device.IsTiler() && + SupportsDepthStencilResolve(device, key.depth_format); return key; } 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 17bd42c642..7b67665829 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -65,8 +65,61 @@ using VideoCore::Surface::SurfaceType; .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; } + + struct ResolveAspects { + bool depth; + bool stencil; + }; + + struct ResolveModes { + VkResolveModeFlagBits depth; + VkResolveModeFlagBits stencil; + }; + + constexpr ResolveAspects GetResolveAspects(PixelFormat format) { + const SurfaceType surface_type = GetSurfaceType(format); + return ResolveAspects{ + .depth = surface_type == SurfaceType::Depth || + surface_type == SurfaceType::DepthStencil, + .stencil = surface_type == SurfaceType::Stencil || + surface_type == SurfaceType::DepthStencil, + }; + } + + ResolveModes PickResolveModes(const Device& device, PixelFormat format) { + constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + + const ResolveAspects aspects = GetResolveAspects(format); + ResolveModes modes{ + .depth = VK_RESOLVE_MODE_NONE, + .stencil = VK_RESOLVE_MODE_NONE, + }; + if (aspects.depth && (device.GetDepthResolveModes() & mode) != 0) { + modes.depth = mode; + } + if (aspects.stencil && (device.GetStencilResolveModes() & mode) != 0) { + modes.stencil = mode; + } + return modes; + } } // Anonymous namespace +bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) { + if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) { + return false; + } + const ResolveAspects aspects = GetResolveAspects(depth_format); + if (!aspects.depth && !aspects.stencil) { + return false; + } + const ResolveModes modes = PickResolveModes(device, depth_format); + if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) || + (aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) { + return false; + } + return modes.depth == modes.stencil || device.SupportsIndependentResolveNone(); +} + RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { @@ -75,7 +128,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { if (!is_new) { return *pair->second; } - boost::container::static_vector descriptions; + static constexpr size_t MAX_ATTACHMENTS = + 2 * std::tuple_size_v + 2; + boost::container::static_vector descriptions; std::array references{}; u32 num_attachments{}; u32 num_colors{}; @@ -133,6 +188,21 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { } } } + const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth && + key.samples != VK_SAMPLE_COUNT_1_BIT && + SupportsDepthStencilResolve(*device, key.depth_format); + VkAttachmentReference depth_resolve_reference{}; + if (do_resolve_depth_stencil) { + depth_resolve_reference = VkAttachmentReference{ + .attachment = static_cast(descriptions.size()), + .layout = VK_IMAGE_LAYOUT_GENERAL, + }; + VkAttachmentDescription resolve_desc = + AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); + resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + descriptions.push_back(resolve_desc); + } const VkSubpassDescription subpass{ .flags = 0, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -155,6 +225,107 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { .dependencyFlags = 0, }; const bool can_resume_transform_feedback = device->IsExtTransformFeedbackSupported(); + + if (device->IsKhrCreateRenderPass2Supported()) { + boost::container::static_vector descriptions2; + for (const VkAttachmentDescription& description : descriptions) { + descriptions2.push_back(VkAttachmentDescription2{ + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + .pNext = nullptr, + .flags = description.flags, + .format = description.format, + .samples = description.samples, + .loadOp = description.loadOp, + .storeOp = description.storeOp, + .stencilLoadOp = description.stencilLoadOp, + .stencilStoreOp = description.stencilStoreOp, + .initialLayout = description.initialLayout, + .finalLayout = description.finalLayout, + }); + } + const auto promote = [](const VkAttachmentReference& reference) { + return VkAttachmentReference2{ + .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, + .pNext = nullptr, + .attachment = reference.attachment, + .layout = reference.layout, + .aspectMask = 0, + }; + }; + std::array references2{}; + std::array resolve_references2{}; + for (size_t index = 0; index < references.size(); ++index) { + references2[index] = promote(references[index]); + resolve_references2[index] = promote(resolve_references[index]); + } + const VkAttachmentReference2 depth_reference2 = promote(depth_reference); + const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference); + const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format); + const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE, + .pNext = nullptr, + .depthResolveMode = resolve_modes.depth, + .stencilResolveMode = resolve_modes.stencil, + .pDepthStencilResolveAttachment = &depth_resolve_reference2, + }; + const VkSubpassDescription2 subpass2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, + .pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr, + .flags = 0, + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .viewMask = 0, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = num_attachments, + .pColorAttachments = references2.data(), + .pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr, + .pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr, + .preserveAttachmentCount = 0, + .pPreserveAttachments = nullptr, + }; + const VkMemoryBarrier2 counter_resume_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT, + .srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT, + .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT, + .dstAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT, + }; + VkSubpassDependency2 counter_resume_dependency2{ + .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, + .pNext = nullptr, + .srcSubpass = counter_resume_dependency.srcSubpass, + .dstSubpass = counter_resume_dependency.dstSubpass, + .srcStageMask = counter_resume_dependency.srcStageMask, + .dstStageMask = counter_resume_dependency.dstStageMask, + .srcAccessMask = counter_resume_dependency.srcAccessMask, + .dstAccessMask = counter_resume_dependency.dstAccessMask, + .dependencyFlags = counter_resume_dependency.dependencyFlags, + .viewOffset = 0, + }; + if (device->HasSynchronization2()) { + counter_resume_dependency2.pNext = &counter_resume_barrier; + counter_resume_dependency2.srcStageMask = 0; + counter_resume_dependency2.dstStageMask = 0; + counter_resume_dependency2.srcAccessMask = 0; + counter_resume_dependency2.dstAccessMask = 0; + } + pair->second = device->GetLogical().CreateRenderPass2({ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, + .pNext = nullptr, + .flags = 0, + .attachmentCount = static_cast(descriptions2.size()), + .pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(), + .subpassCount = 1, + .pSubpasses = &subpass2, + .dependencyCount = can_resume_transform_feedback ? 1u : 0u, + .pDependencies = can_resume_transform_feedback ? &counter_resume_dependency2 : nullptr, + .correlatedViewMaskCount = 0, + .pCorrelatedViewMasks = nullptr, + }); + return *pair->second; + } + pair->second = device->GetLogical().CreateRenderPass({ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = nullptr, 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 4769d65f39..0b01fc0194 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -22,6 +22,7 @@ struct RenderPassKey { VideoCore::Surface::PixelFormat depth_format; VkSampleCountFlagBits samples; bool resolve_color; + bool resolve_depth_stencil; u32 color_clear_mask; bool depth_stencil_clear; u32 color_discard_mask; @@ -47,7 +48,8 @@ struct hash { (static_cast(key.color_clear_mask) << 16) | (static_cast(key.color_discard_mask) << 24) | (static_cast(key.resolve_color) << 32) | - (static_cast(key.depth_stencil_clear) << 33); + (static_cast(key.depth_stencil_clear) << 33) | + (static_cast(key.resolve_depth_stencil) << 34); size_t seed = 0; Common::HashCombine(seed, formats); Common::HashCombine(seed, state); @@ -60,6 +62,9 @@ namespace Vulkan { class Device; +[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device, + VideoCore::Surface::PixelFormat depth_format); + class RenderPassCache { public: explicit RenderPassCache(const Device& device_); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index bcf4fa1021..2f15a90977 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -993,13 +993,25 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { } VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format, - VkExtent2D extent, u32 layers) { + VkExtent2D extent, u32 layers, + VkImageAspectFlags aspect_mask) { ResolveShadow& shadow = resolve_shadows[msaa_image]; if (shadow.image && shadow.format == format && shadow.extent.width == extent.width && - shadow.extent.height == extent.height && shadow.layers == layers) { + shadow.extent.height == extent.height && shadow.layers == layers && + shadow.aspect_mask == aspect_mask) { shadow.up_to_date = true; return *shadow.view; } + VkImageUsageFlags shadow_usage = + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { + shadow_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } else { + shadow_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if (shadow.image) { + pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(shadow)); + } shadow.image = memory_allocator.CreateImage(VkImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, @@ -1011,8 +1023,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk .arrayLayers = layers, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + .usage = shadow_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -1027,7 +1038,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk .format = format, .components{}, .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, @@ -1037,6 +1048,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk shadow.format = format; shadow.extent = extent; shadow.layers = layers; + shadow.aspect_mask = aspect_mask; shadow.up_to_date = true; return *shadow.view; } @@ -1058,7 +1070,14 @@ void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) { } void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) { - resolve_shadows.erase(msaa_image); + const auto it = resolve_shadows.find(msaa_image); + if (it == resolve_shadows.end()) { + return; + } + if (it->second.image) { + pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(it->second)); + } + resolve_shadows.erase(it); } void TextureCacheRuntime::BarrierFeedbackLoop() { @@ -1589,31 +1608,41 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, std::span copies) { const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1; const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples; - if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT || - VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) { - UNIMPLEMENTED_MSG("Copying images with different samples is not supported."); - return; - } if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 && src.info.format == dst.info.format) { const VideoCommon::ImageCopy& copy = copies.front(); const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle()); - if (shadow != nullptr && copy.src_offset.x == 0 && copy.src_offset.y == 0 && + if (shadow != nullptr && shadow->aspect_mask == dst.AspectMask() && + copy.src_offset.x == 0 && copy.src_offset.y == 0 && copy.src_subresource.base_level == 0 && static_cast(copy.extent.width) <= shadow->extent.width && - static_cast(copy.extent.height) <= shadow->extent.height) { + static_cast(copy.extent.height) <= shadow->extent.height && + static_cast(copy.src_subresource.base_layer + copy.src_subresource.num_layers) <= + shadow->layers) { + const VkImageAspectFlags aspect_mask = shadow->aspect_mask; + VkPipelineStageFlags attachment_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkAccessFlags attachment_write = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkAccessFlags attachment_read_write = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { + attachment_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + attachment_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + attachment_read_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } const VkImage shadow_image = *shadow->image; const VkImage dst_image = dst.Handle(); const VkImageCopy region{ .srcSubresource{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .mipLevel = 0, .baseArrayLayer = static_cast(copy.src_subresource.base_layer), .layerCount = static_cast(copy.src_subresource.num_layers), }, .srcOffset = {0, 0, 0}, .dstSubresource{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect_mask, .mipLevel = static_cast(copy.dst_subresource.base_level), .baseArrayLayer = static_cast(copy.dst_subresource.base_layer), .layerCount = static_cast(copy.dst_subresource.num_layers), @@ -1622,26 +1651,27 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .extent = {copy.extent.width, copy.extent.height, 1}, }; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([shadow_image, dst_image, region](vk::CommandBuffer cmdbuf) { + scheduler.Record([shadow_image, dst_image, region, aspect_mask, attachment_stage, + attachment_write, + attachment_read_write](vk::CommandBuffer cmdbuf) { const std::array pre_barriers{ VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .srcAccessMask = attachment_write, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = shadow_image, - .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | attachment_write | VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, @@ -1649,7 +1679,7 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = dst_image, - .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, }; @@ -1664,28 +1694,25 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = shadow_image, - .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | attachment_read_write | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = dst_image, - .subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, + .subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS}, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, + cmdbuf.PipelineBarrier(attachment_stage | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr, pre_barriers); cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, @@ -1697,6 +1724,11 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src, return; } } + if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT || + VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) { + UNIMPLEMENTED_MSG("Copying images with different samples is not supported."); + return; + } blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(), src.info.format, num_samples, copies, msaa_to_non_msaa); } @@ -1721,6 +1753,9 @@ void TextureCacheRuntime::TickFrame() { std::erase_if(pending_msaa_images, [this](const auto& pending) { return scheduler.IsFree(pending.first); }); + std::erase_if(pending_resolve_shadows, [this](const auto& pending) { + return scheduler.IsFree(pending.first); + }); } Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, @@ -2663,7 +2698,7 @@ Framebuffer::~Framebuffer() = default; void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, std::span color_buffers, ImageView* depth_buffer, bool is_rescaled_) { - boost::container::small_vector attachments; + boost::container::small_vector attachments; RenderPassKey renderpass_key{}; s32 num_layers = 1; @@ -2692,6 +2727,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, ++num_images; } const size_t num_colors = attachments.size(); + VkImage depth_image = VK_NULL_HANDLE; + VkImageAspectFlags depth_aspect_mask = 0; if (depth_buffer) { width = (std::min)(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) : depth_buffer->size.width); @@ -2707,6 +2744,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, ++num_images; has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; + depth_image = depth_buffer->ImageHandle(); + depth_aspect_mask = subresource_range.aspectMask; } else { renderpass_key.depth_format = PixelFormat::Invalid; } @@ -2715,6 +2754,12 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler(); renderpass_key.resolve_color = do_resolve_color; + const bool do_resolve_depth_stencil = + samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE && + runtime.device.IsTiler() && + SupportsDepthStencilResolve(runtime.device, renderpass_key.depth_format); + renderpass_key.resolve_depth_stencil = do_resolve_depth_stencil; + discard_msaa_color = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color; @@ -2735,8 +2780,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format; if (ENABLE_MSAA_RESOLVE_CONSUME) { const VkImage msaa_image = images[rt_map[index]]; - attachments.push_back(runtime.GetOrCreateResolveShadow(msaa_image, vk_format, - render_area, layers)); + attachments.push_back(runtime.GetOrCreateResolveShadow( + msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT)); continue; } VkImageCreateInfo resolve_ci{ @@ -2781,6 +2826,16 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, } } + if (do_resolve_depth_stencil) { + const u32 layers = static_cast((std::max)(num_layers, 1)); + const VkFormat vk_format = + MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, + renderpass_key.depth_format) + .format; + attachments.push_back(runtime.GetOrCreateResolveShadow(depth_image, vk_format, render_area, + layers, depth_aspect_mask)); + } + num_color_buffers = static_cast(num_colors); framebuffer = runtime.device.GetLogical().CreateFramebuffer({ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 881fcc4de0..5dc8f81e4b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -117,11 +117,13 @@ public: VkFormat format = VK_FORMAT_UNDEFINED; VkExtent2D extent{}; u32 layers = 0; + VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; bool up_to_date = false; }; [[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format, - VkExtent2D extent, u32 layers); + VkExtent2D extent, u32 layers, + VkImageAspectFlags aspect_mask); [[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const; @@ -158,6 +160,7 @@ public: std::array buffers{}; std::vector> pending_msaa_images; ankerl::unordered_dense::map resolve_shadows; + std::vector> pending_resolve_shadows; }; class Framebuffer { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 6506bdb596..ff8f834785 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1075,6 +1075,12 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); + extensions.depth_stencil_resolve = + extensions.depth_stencil_resolve && + (instance_version >= VK_API_VERSION_1_2 || extensions.create_renderpass2); + RemoveExtensionIfUnsuitable(extensions.depth_stencil_resolve, + VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); + if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) { loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME); @@ -1222,6 +1228,11 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; SetNext(next, properties.push_descriptor); } + if (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) { + properties.depth_stencil_resolve.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES; + SetNext(next, properties.depth_stencil_resolve); + } if (extensions.descriptor_buffer) { properties.descriptor_buffer.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 2602d97103..2a51024645 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -91,6 +91,8 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ EXTENSION(EXT, TOOLING_INFO, tooling_info) \ EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ + EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \ + EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ @@ -617,6 +619,37 @@ FN_MAX_LIMIT_LIST return extensions.shader_stencil_export; } + /// Returns true if the device supports VK_KHR_create_renderpass2. + bool IsKhrCreateRenderPass2Supported() const { + return extensions.create_renderpass2 || instance_version >= VK_API_VERSION_1_2; + } + + /// Returns true if the device supports VK_KHR_depth_stencil_resolve. + bool IsKhrDepthStencilResolveSupported() const { + return (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) && + IsKhrCreateRenderPass2Supported(); + } + + /// Returns the supported resolve modes for the depth aspect. + VkResolveModeFlags GetDepthResolveModes() const { + return properties.depth_stencil_resolve.supportedDepthResolveModes; + } + + /// Returns the supported resolve modes for the stencil aspect. + VkResolveModeFlags GetStencilResolveModes() const { + return properties.depth_stencil_resolve.supportedStencilResolveModes; + } + + /// Returns true if the depth and stencil aspects may resolve with different modes. + bool SupportsIndependentResolve() const { + return properties.depth_stencil_resolve.independentResolve == VK_TRUE; + } + + /// Returns true if only one of the depth and stencil aspects may be resolved. + bool SupportsIndependentResolveNone() const { + return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE; + } + /// Returns true if depth/stencil operations can be performed efficiently. /// Either through shader export or hardware blits. bool CanPerformDepthStencilOperations() const { @@ -1172,6 +1205,7 @@ private: VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; + VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{}; VkPhysicalDeviceProperties properties{}; }; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 24f9da0a9e..33173e956d 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -184,6 +184,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCreatePipelineLayout); X(vkCreateQueryPool); X(vkCreateRenderPass); + X(vkCreateRenderPass2); X(vkCreateSampler); X(vkCreateSemaphore); X(vkCreateShaderModule); @@ -270,6 +271,11 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { if (!dld.vkQueueSubmit2) { Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device); } + + // Render pass creation v2 is core in Vulkan 1.2, otherwise requires VK_KHR_create_renderpass2 + if (!dld.vkCreateRenderPass2) { + Proc(dld.vkCreateRenderPass2, dld, "vkCreateRenderPass2KHR", device); + } #undef X } @@ -725,6 +731,12 @@ RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const { return RenderPass(object, handle, *dld); } +RenderPass Device::CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const { + VkRenderPass object; + Check(dld->vkCreateRenderPass2(handle, &ci, nullptr, &object)); + return RenderPass(object, handle, *dld); +} + DescriptorSetLayout Device::CreateDescriptorSetLayout( const VkDescriptorSetLayoutCreateInfo& ci) const { VkDescriptorSetLayout object; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index c2d867838d..dd9a3a21d5 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -300,6 +300,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; PFN_vkCreateQueryPool vkCreateQueryPool{}; PFN_vkCreateRenderPass vkCreateRenderPass{}; + PFN_vkCreateRenderPass2 vkCreateRenderPass2{}; PFN_vkCreateSampler vkCreateSampler{}; PFN_vkCreateSemaphore vkCreateSemaphore{}; PFN_vkCreateShaderModule vkCreateShaderModule{}; @@ -1045,6 +1046,8 @@ public: [[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; + [[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const; + [[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout( const VkDescriptorSetLayoutCreateInfo& ci) const;