From 480104dc50b0a5fb62096eece87397fb09aef57e Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 31 Jan 2026 20:55:07 -0400 Subject: [PATCH] [vulkan] Follow-up for the VK_KHR_dynamic_rendering implementation --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 48 ++++++++++++++++- .../renderer_vulkan/vk_scheduler.cpp | 51 ++++++++++++------- src/video_core/renderer_vulkan/vk_scheduler.h | 3 +- .../renderer_vulkan/vk_texture_cache.cpp | 45 ++++++++++++++++ .../renderer_vulkan/vk_texture_cache.h | 11 +++- .../vulkan_common/vulkan_device.cpp | 8 +++ src/video_core/vulkan_common/vulkan_device.h | 4 +- 7 files changed, 146 insertions(+), 24 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index d36553da4a..896897f6b3 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -25,6 +25,7 @@ #include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/shader_notify.h" #include "video_core/texture_cache/texture_cache.h" +#include "video_core/surface.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/gpu_logging/gpu_logging.h" #include "common/settings.h" @@ -138,6 +139,10 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state) { return key; } +VkFormat DecodeVkFormat(const Device& device, PixelFormat format) { + return MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, format).format; +} + size_t NumAttachments(const FixedPipelineState& state) { size_t num{}; for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { @@ -942,10 +947,49 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } + const bool use_dynamic_rendering = device.IsDynamicRenderingSupported(); + static_vector color_formats; + if (use_dynamic_rendering) { + const size_t num_colors = NumAttachments(key.state); + for (size_t index = 0; index < num_colors; ++index) { + const auto rt_format = DecodeFormat(key.state.color_formats[index]); + if (rt_format == PixelFormat::Invalid) { + continue; + } + color_formats.push_back(DecodeVkFormat(device, rt_format)); + } + } + VkFormat depth_attachment_format = VK_FORMAT_UNDEFINED; + VkFormat stencil_attachment_format = VK_FORMAT_UNDEFINED; + if (use_dynamic_rendering && key.state.depth_enabled != 0) { + const auto depth_format = static_cast(key.state.depth_format.Value()); + const PixelFormat depth_pixel_format = PixelFormatFromDepthFormat(depth_format); + const auto surface_type = VideoCore::Surface::GetFormatType(depth_pixel_format); + const VkFormat vk_depth_format = DecodeVkFormat(device, depth_pixel_format); + if (surface_type == VideoCore::Surface::SurfaceType::Depth || + surface_type == VideoCore::Surface::SurfaceType::DepthStencil) { + depth_attachment_format = vk_depth_format; + } + if (surface_type == VideoCore::Surface::SurfaceType::Stencil || + surface_type == VideoCore::Surface::SurfaceType::DepthStencil) { + stencil_attachment_format = vk_depth_format; + } + } + + VkPipelineRenderingCreateInfo pipeline_rendering_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .pNext = nullptr, + .viewMask = 0, + .colorAttachmentCount = static_cast(color_formats.size()), + .pColorAttachmentFormats = color_formats.data(), + .depthAttachmentFormat = depth_attachment_format, + .stencilAttachmentFormat = stencil_attachment_format, + }; + pipeline = device.GetLogical().CreateGraphicsPipeline( { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = use_dynamic_rendering ? &pipeline_rendering_ci : nullptr, .flags = flags, .stageCount = static_cast(shader_stages.size()), .pStages = shader_stages.data(), @@ -959,7 +1003,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pColorBlendState = &color_blend_ci, .pDynamicState = &dynamic_state_ci, .layout = *pipeline_layout, - .renderPass = render_pass, + .renderPass = use_dynamic_rendering ? VK_NULL_HANDLE : render_pass, .subpass = 0, .basePipelineHandle = nullptr, .basePipelineIndex = 0, diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index e526d606dc..00a05362cd 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -108,6 +108,7 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { const VkRenderPass renderpass = framebuffer->RenderPass(); const VkFramebuffer framebuffer_handle = framebuffer->Handle(); const VkExtent2D render_area = framebuffer->RenderArea(); + const bool use_dynamic_rendering = device.IsDynamicRenderingSupported(); if (renderpass == state.renderpass && framebuffer_handle == state.framebuffer && render_area.width == state.render_area.width && render_area.height == state.render_area.height) { @@ -127,25 +128,31 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); } - Record([renderpass, framebuffer_handle, render_area](vk::CommandBuffer cmdbuf) { - const VkRenderPassBeginInfo renderpass_bi{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .pNext = nullptr, - .renderPass = renderpass, - .framebuffer = framebuffer_handle, - .renderArea = - { - .offset = {.x = 0, .y = 0}, - .extent = render_area, - }, - .clearValueCount = 0, - .pClearValues = nullptr, - }; - cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); - }); + if (use_dynamic_rendering) { + const VkRenderingInfo rendering_info = framebuffer->RenderingInfo(); + Record([rendering_info](vk::CommandBuffer cmdbuf) { cmdbuf.BeginRendering(&rendering_info); }); + } else { + Record([renderpass, framebuffer_handle, render_area](vk::CommandBuffer cmdbuf) { + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = renderpass, + .framebuffer = framebuffer_handle, + .renderArea = + { + .offset = {.x = 0, .y = 0}, + .extent = render_area, + }, + .clearValueCount = 0, + .pClearValues = nullptr, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); + }); + } num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); renderpass_image_ranges = framebuffer->ImageRanges(); + state.using_dynamic_rendering = use_dynamic_rendering; } void Scheduler::RequestOutsideRenderPassOperationContext() { @@ -334,8 +341,9 @@ void Scheduler::EndRenderPass() query_cache->NotifySegment(false); Record([num_images = num_renderpass_images, - images = renderpass_images, - ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) { + images = renderpass_images, + ranges = renderpass_image_ranges, + using_dynamic = state.using_dynamic_rendering](vk::CommandBuffer cmdbuf) { std::array barriers; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; @@ -371,7 +379,11 @@ void Scheduler::EndRenderPass() .subresourceRange = range, }; } - cmdbuf.EndRenderPass(); + if (using_dynamic) { + cmdbuf.EndRendering(); + } else { + cmdbuf.EndRenderPass(); + } cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, @@ -384,6 +396,7 @@ void Scheduler::EndRenderPass() }); state.renderpass = nullptr; + state.using_dynamic_rendering = false; num_renderpass_images = 0; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 5216a436c8..96dbfb9ff7 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.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 @@ -218,6 +218,7 @@ private: bool is_rescaling = false; bool rescaling_defined = false; bool needs_state_enable_refresh = false; + bool using_dynamic_rendering = false; }; void WorkerThread(std::stop_token stop_token); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 39a43d5950..9d6f3e1c8b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2384,11 +2384,24 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, RenderPassKey renderpass_key{}; s32 num_layers = 1; + rendering_info = { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pNext = nullptr, + .flags = 0, + .renderArea = { + .offset = {0, 0}, + .extent = render_area, + }, + .layerCount = 1, + .viewMask = 0, + }; + is_rescaled = is_rescaled_; const auto& resolution = runtime.resolution; u32 width = (std::numeric_limits::max)(); u32 height = (std::numeric_limits::max)(); + u32 color_attachment_count = 0; for (size_t index = 0; index < NUM_RT; ++index) { const ImageView* const color_buffer = color_buffers[index]; if (!color_buffer) { @@ -2406,7 +2419,20 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, image_ranges[num_images] = MakeSubresourceRange(color_buffer); rt_map[index] = num_images; samples = color_buffer->Samples(); + color_attachment_infos[color_attachment_count] = VkRenderingAttachmentInfo{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = color_buffer->RenderTarget(), + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = VK_NULL_HANDLE, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {}, + }; ++num_images; + ++color_attachment_count; } const size_t num_colors = attachments.size(); if (depth_buffer) { @@ -2424,6 +2450,18 @@ 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_attachment_info = VkRenderingAttachmentInfo{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = depth_buffer->RenderTarget(), + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = VK_NULL_HANDLE, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {}, + }; } else { renderpass_key.depth_format = PixelFormat::Invalid; } @@ -2433,6 +2471,13 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime, render_area.width = (std::min)(render_area.width, width); render_area.height = (std::min)(render_area.height, height); + rendering_info.layerCount = static_cast((std::max)(num_layers, 1)); + rendering_info.renderArea.extent = render_area; + rendering_info.colorAttachmentCount = color_attachment_count; + rendering_info.pColorAttachments = color_attachment_count > 0 ? color_attachment_infos.data() : nullptr; + rendering_info.pDepthAttachment = has_depth ? &depth_attachment_info : nullptr; + rendering_info.pStencilAttachment = has_stencil ? &depth_attachment_info : nullptr; + 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 4bb9687ab0..d67a5e3293 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 @@ -169,6 +169,11 @@ public: return renderpass; } + /// Returns the cached rendering info used for dynamic rendering. + [[nodiscard]] const VkRenderingInfo& RenderingInfo() const noexcept { + return rendering_info; + } + [[nodiscard]] VkExtent2D RenderArea() const noexcept { return render_area; } @@ -222,6 +227,10 @@ private: bool has_depth{}; bool has_stencil{}; bool is_rescaled{}; + + std::array color_attachment_infos{}; + VkRenderingAttachmentInfo depth_attachment_info{}; + VkRenderingInfo rendering_info{}; }; class Image : public VideoCommon::ImageBase { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index d985671e51..650ffcb1dd 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1307,6 +1307,14 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + // VK_KHR_dynamic_rendering + if (!features.dynamic_rendering.dynamicRendering && extensions.dynamic_rendering) { + LOG_WARNING(Render_Vulkan, + "VK_KHR_dynamic_rendering reported but dynamicRendering feature not available, disabling"); + RemoveExtensionFeature(extensions.dynamic_rendering, features.dynamic_rendering, + VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); + } + // VK_EXT_descriptor_buffer extensions.descriptor_buffer = loaded_extensions.contains(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.descriptor_buffer, VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index f08c8125bf..990e44a967 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -38,6 +38,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore) #define FOR_EACH_VK_FEATURE_1_3(FEATURE) \ + FEATURE(KHR, DynamicRendering, DYNAMIC_RENDERING, dynamic_rendering) \ FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, image_robustness) \ FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \ shader_demote_to_helper_invocation) \ @@ -356,7 +357,8 @@ public: /// Returns true if the device supports VK_KHR_dynamic_rendering. bool IsDynamicRenderingSupported() const { - return extensions.dynamic_rendering; + return features.dynamic_rendering.dynamicRendering && + (instance_version >= VK_API_VERSION_1_3 || extensions.dynamic_rendering); } /// Returns true if ASTC is natively supported.