diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 79cb3ae81..6cb5f32fd 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -529,7 +529,8 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR, vk::PhysicalDeviceCustomBorderColorFeaturesEXT, vk::PhysicalDeviceIndexTypeUint8FeaturesEXT, - vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT>(); + vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT, + vk::PhysicalDeviceDynamicRenderingFeaturesKHR>(); const vk::StructureChain properties_chain = physical_device.getProperties2(); @@ -568,6 +569,7 @@ bool Instance::CreateDevice() { image_format_list = AddExtension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME); pipeline_creation_feedback = AddExtension(VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME); bool has_portability_subset = AddExtension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); + bool has_dynamic_rendering = AddExtension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); bool has_extended_dynamic_state = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); bool has_extended_dynamic_state2 = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); bool has_extended_dynamic_state3 = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); @@ -656,6 +658,7 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{}, vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{}, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{}, + vk::PhysicalDeviceDynamicRenderingFeaturesKHR{}, vk::PhysicalDeviceCustomBorderColorFeaturesEXT{}, vk::PhysicalDeviceIndexTypeUint8FeaturesEXT{}, vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT{}, @@ -714,6 +717,13 @@ bool Instance::CreateDevice() { device_chain.unlink(); } + if (has_dynamic_rendering) { + FEAT_SET(vk::PhysicalDeviceDynamicRenderingFeaturesKHR, dynamicRendering, + dynamic_rendering); + } else { + device_chain.unlink(); + } + if (has_custom_border_color) { FEAT_SET(vk::PhysicalDeviceCustomBorderColorFeaturesEXT, customBorderColors, custom_border_color) diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index cabbcaf2b..0334f0594 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -148,6 +148,11 @@ public: return extended_dynamic_state3_color_write_mask; } + /// Returns true when VK_KHR_dynamic_rendering is supported + bool IsDynamicRenderingSupported() const { + return dynamic_rendering; + } + /// Returns true when VK_KHR_push_descriptors is supported bool IsPushDescriptorsSupported() const { return push_descriptors; @@ -305,6 +310,7 @@ private: bool extended_dynamic_state3_color_blend_eq{}; bool extended_dynamic_state3_color_write_mask{}; bool push_descriptors{}; + bool dynamic_rendering{}; bool custom_border_color{}; bool index_type_uint8{}; bool image_format_list{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 9531ab24a..6999bfc9e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -110,10 +110,8 @@ PipelineCache::GraphicsPipeline::GraphicsPipeline( const Instance& instance_, RenderpassCache& renderpass_cache_, const PipelineInfo& info_, vk::PipelineCache pipeline_cache_, vk::PipelineLayout layout_, std::array stages_, Common::ThreadWorker* worker_) - : instance{instance_}, worker{worker_}, pipeline_layout{layout_}, - pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_}, - renderpass{renderpass_cache_.GetRenderpass(info.attachments.color_format, - info.attachments.depth_format, false)} { + : instance{instance_}, renderpass_cache{renderpass_cache_}, worker{worker_}, + pipeline_layout{layout_}, pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_} { // Ask the driver if it can give us the pipeline quickly if (Build(true)) { @@ -322,12 +320,6 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) { .flags = vk::PipelineCreationFeedbackFlagBits::eValid, }; - const vk::PipelineCreationFeedbackCreateInfoEXT creation_feedback_info = { - .pPipelineCreationFeedback = &creation_feedback, - .pipelineStageCreationFeedbackCount = shader_count, - .pPipelineStageCreationFeedbacks = creation_stage_feedback.data(), - }; - vk::GraphicsPipelineCreateInfo pipeline_info = { .stageCount = shader_count, .pStages = shader_stages.data(), @@ -340,17 +332,48 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) { .pColorBlendState = &color_blending, .pDynamicState = &dynamic_info, .layout = pipeline_layout, - .renderPass = renderpass, }; + if (!instance.IsDynamicRenderingSupported()) { + pipeline_info.renderPass = renderpass_cache.GetRenderpass( + info.attachments.color_format, info.attachments.depth_format, false); + } + if (fail_on_compile_required) { pipeline_info.flags |= vk::PipelineCreateFlagBits::eFailOnPipelineCompileRequiredEXT; } - if (instance.IsPipelineCreationFeedbackSupported()) { - pipeline_info.pNext = &creation_feedback_info; + + const auto [color, depth] = info.attachments; + const auto& color_traits = instance.GetTraits(color); + const auto& depth_traits = instance.GetTraits(depth); + const u32 color_attachment_count = color != VideoCore::PixelFormat::Invalid ? 1u : 0u; + + vk::StructureChain pipeline_chain = { + pipeline_info, + vk::PipelineCreationFeedbackCreateInfoEXT{ + .pPipelineCreationFeedback = &creation_feedback, + .pipelineStageCreationFeedbackCount = shader_count, + .pPipelineStageCreationFeedbacks = creation_stage_feedback.data(), + }, + vk::PipelineRenderingCreateInfoKHR{ + .colorAttachmentCount = color_attachment_count, + .pColorAttachmentFormats = &color_traits.native, + .depthAttachmentFormat = depth_traits.native, + .stencilAttachmentFormat = depth_traits.aspect & vk::ImageAspectFlagBits::eStencil + ? depth_traits.native + : vk::Format::eUndefined, + }, + }; + + if (!instance.IsPipelineCreationFeedbackSupported()) { + pipeline_chain.unlink(); + } + if (!instance.IsDynamicRenderingSupported()) { + pipeline_chain.unlink(); } - const vk::ResultValue result = device.createGraphicsPipeline(pipeline_cache, pipeline_info); + const vk::ResultValue result = + device.createGraphicsPipeline(pipeline_cache, pipeline_chain.get()); if (result.result == vk::Result::eSuccess) { pipeline = result.value; } else if (result.result == vk::Result::eErrorPipelineCompileRequiredEXT) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 8cdfff01e..1ccd5cd6a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -162,6 +162,7 @@ class PipelineCache { private: const Instance& instance; + RenderpassCache& renderpass_cache; Common::ThreadWorker* worker; vk::Pipeline pipeline; @@ -170,7 +171,6 @@ class PipelineCache { PipelineInfo info; std::array stages; - vk::RenderPass renderpass; }; public: diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp index 5d5f97e3c..cae4b4c72 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp @@ -12,7 +12,8 @@ namespace Vulkan { RenderpassCache::RenderpassCache(const Instance& instance, Scheduler& scheduler) - : instance{instance}, scheduler{scheduler} {} + : instance{instance}, scheduler{scheduler}, dynamic_rendering{ + instance.IsDynamicRenderingSupported()} {} RenderpassCache::~RenderpassCache() { vk::Device device = instance.GetDevice(); @@ -39,6 +40,10 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) { ASSERT(color || depth_stencil); + if (dynamic_rendering) { + return BeginRendering(color, depth_stencil, render_area, do_clear, clear); + } + u32 width = UINT32_MAX; u32 height = UINT32_MAX; u32 cursor = 0; @@ -81,13 +86,14 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth .clear = clear, }; + const u64 new_state_hash = Common::ComputeStructHash64(new_state); const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass); - if (current_state == new_state && !is_dirty) { + if (state_hash == new_state_hash && rendering && !is_dirty) { cmd_count++; return; } - if (current_state.renderpass) { + if (rendering) { ExitRenderpass(); } @@ -104,16 +110,23 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth }); scheduler.MarkStateNonDirty(StateFlags::Renderpass); - current_state = new_state; + state_hash = new_state_hash; + rendering = true; } void RenderpassCache::ExitRenderpass() { - if (!current_state.renderpass) { + if (!rendering) { return; } - scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.endRenderPass(); }); - current_state = {}; + rendering = false; + scheduler.Record([dynamic_rendering = dynamic_rendering](vk::CommandBuffer cmdbuf) { + if (dynamic_rendering) { + cmdbuf.endRenderingKHR(); + } else { + cmdbuf.endRenderPass(); + } + }); // The Mali guide recommends flushing at the end of each major renderpass // Testing has shown this has a significant effect on rendering performance @@ -123,6 +136,78 @@ void RenderpassCache::ExitRenderpass() { } } +void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_stencil, + vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) { + RenderingState new_state = { + .render_area = render_area, + .clear = clear, + .do_clear = do_clear, + }; + + if (color) { + new_state.color_view = color->GetFramebufferView(); + } + if (depth_stencil) { + new_state.depth_view = depth_stencil->GetFramebufferView(); + } + + const u64 new_state_hash = Common::ComputeStructHash64(new_state); + const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass); + if (state_hash == new_state_hash && rendering && !is_dirty) { + cmd_count++; + return; + } + + if (rendering) { + ExitRenderpass(); + } + + const bool has_stencil = + depth_stencil && depth_stencil->type == VideoCore::SurfaceType::DepthStencil; + scheduler.Record([new_state, has_stencil](vk::CommandBuffer cmdbuf) { + u32 cursor = 0; + std::array infos{}; + + const auto Prepare = [&](vk::ImageView image_view) { + if (!image_view) { + cursor++; + return; + } + + infos[cursor++] = vk::RenderingAttachmentInfoKHR{ + .imageView = image_view, + .imageLayout = vk::ImageLayout::eGeneral, + .loadOp = + new_state.do_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = new_state.clear, + }; + }; + + Prepare(new_state.color_view); + Prepare(new_state.depth_view); + + const u32 color_attachment_count = new_state.color_view ? 1u : 0u; + const vk::RenderingAttachmentInfoKHR* depth_info = + new_state.depth_view ? &infos[1] : nullptr; + const vk::RenderingAttachmentInfoKHR* stencil_info = has_stencil ? &infos[1] : nullptr; + const vk::RenderingInfoKHR rendering_info = { + .renderArea = new_state.render_area, + .layerCount = 1, + .colorAttachmentCount = color_attachment_count, + .pColorAttachments = &infos[0], + .pDepthAttachment = depth_info, + .pStencilAttachment = stencil_info, + }; + + cmdbuf.beginRenderingKHR(rendering_info); + }); + + scheduler.MarkStateNonDirty(StateFlags::Renderpass); + state_hash = new_state_hash; + rendering = true; +} + void RenderpassCache::CreatePresentRenderpass(vk::Format format) { if (!present_renderpass) { present_renderpass = diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.h b/src/video_core/renderer_vulkan/vk_renderpass_cache.h index 4c6e79610..a18830133 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.h +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/hash.h" #include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/renderer_vulkan/vk_common.h" @@ -65,6 +66,10 @@ public: } private: + /// Begins a new rendering scope using dynamic rendering + void BeginRendering(Surface* const color, Surface* const depth_stencil, vk::Rect2D render_area, + bool do_clear, vk::ClearValue clear); + /// Creates a renderpass configured appropriately and stores it in cached_renderpasses vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth, vk::AttachmentLoadOp load_op, vk::ImageLayout initial_layout, @@ -85,13 +90,27 @@ private: } }; + struct RenderingState { + vk::ImageView color_view; + vk::ImageView depth_view; + vk::Rect2D render_area; + vk::ClearValue clear; + bool do_clear; + + [[nodiscard]] bool operator==(const RenderpassState& other) const { + return std::memcmp(this, &other, sizeof(RenderpassState)) == 0; + } + }; + const Instance& instance; Scheduler& scheduler; - RenderpassState current_state{}; vk::RenderPass present_renderpass{}; vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2]; std::unordered_map framebuffers; + bool rendering = false; + bool dynamic_rendering = false; u32 cmd_count{}; + u64 state_hash{}; }; } // namespace Vulkan