diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 2c1aa740e7..f5fea5cac3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -202,6 +202,11 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), wfi_event(device.GetLogical().CreateEvent()) { scheduler.SetQueryCache(query_cache); + + // Log multi-draw support + if (device.IsExtMultiDrawSupported()) { + LOG_INFO(Render_Vulkan, "VK_EXT_multi_draw is enabled for optimized draw calls"); + } } RasterizerVulkan::~RasterizerVulkan() = default; @@ -243,16 +248,44 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { const u32 num_instances{instance_count}; const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs); const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed, polygon_mode)}; - scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { - if (draw_params.is_indexed) { - cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, - draw_params.first_index, draw_params.base_vertex, - draw_params.base_instance); - } else { - cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances, - draw_params.base_vertex, draw_params.base_instance); - } - }); + + // Use VK_EXT_multi_draw if available (single draw becomes multi-draw with count=1) + if (device.IsExtMultiDrawSupported()) { + scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { + if (draw_params.is_indexed) { + // Use multi-draw indexed with single draw + const VkMultiDrawIndexedInfoEXT multi_draw_info{ + .firstIndex = draw_params.first_index, + .indexCount = draw_params.num_vertices, + }; + const int32_t vertex_offset = static_cast(draw_params.base_vertex); + cmdbuf.DrawMultiIndexedEXT(1, &multi_draw_info, draw_params.num_instances, + draw_params.base_instance, + sizeof(VkMultiDrawIndexedInfoEXT), &vertex_offset); + } else { + // Use multi-draw with single draw + const VkMultiDrawInfoEXT multi_draw_info{ + .firstVertex = draw_params.base_vertex, + .vertexCount = draw_params.num_vertices, + }; + cmdbuf.DrawMultiEXT(1, &multi_draw_info, draw_params.num_instances, + draw_params.base_instance, + sizeof(VkMultiDrawInfoEXT)); + } + }); + } else { + // Fallback to standard draw calls + scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { + if (draw_params.is_indexed) { + cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, + draw_params.first_index, draw_params.base_vertex, + draw_params.base_instance); + } else { + cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances, + draw_params.base_vertex, draw_params.base_instance); + } + }); + } }); } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index fc22b90e3b..d89379f522 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1126,6 +1126,11 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR; SetNext(next, properties.maintenance5); } + if (extensions.multi_draw) { + properties.multi_draw.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT; + SetNext(next, properties.multi_draw); + } // Perform the property fetch. physical.GetProperties2(properties2); @@ -1351,6 +1356,17 @@ void Device::RemoveUnsuitableExtensions() { features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + // VK_EXT_multi_draw + extensions.multi_draw = features.multi_draw.multiDraw; + + if (extensions.multi_draw) { + LOG_INFO(Render_Vulkan, "VK_EXT_multi_draw: maxMultiDrawCount={}", + properties.multi_draw.maxMultiDrawCount); + } + + RemoveExtensionFeatureIfUnsuitable(extensions.multi_draw, features.multi_draw, + VK_EXT_MULTI_DRAW_EXTENSION_NAME); + // VK_KHR_pipeline_executable_properties if (Settings::values.renderer_shader_feedback.GetValue()) { extensions.pipeline_executable_properties = diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 5b608a5c6a..8f200fcff4 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -56,6 +56,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \ FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \ FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \ + FEATURE(EXT, MultiDraw, MULTI_DRAW, multi_draw) \ FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \ primitive_topology_list_restart) \ FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \ @@ -818,6 +819,11 @@ public: return extensions.maintenance6; } + /// Returns true if the device supports VK_EXT_multi_draw. + bool IsExtMultiDrawSupported() const { + return extensions.multi_draw; + } + /// Returns true if the device supports VK_KHR_maintenance7. bool IsKhrMaintenance7Supported() const { return extensions.maintenance7; @@ -950,6 +956,7 @@ private: VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; + VkPhysicalDeviceMultiDrawPropertiesEXT multi_draw{}; VkPhysicalDeviceProperties properties{}; }; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index b77d01711a..2f78f1818b 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -116,6 +116,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdDrawIndirectCount); X(vkCmdDrawIndexedIndirectCount); X(vkCmdDrawIndirectByteCountEXT); + X(vkCmdDrawMultiEXT); + X(vkCmdDrawMultiIndexedEXT); X(vkCmdEndConditionalRenderingEXT); X(vkCmdEndQuery); X(vkCmdEndRenderPass); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 39396b3279..bdef05c4c5 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -216,6 +216,8 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount{}; PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount{}; PFN_vkCmdDrawIndirectByteCountEXT vkCmdDrawIndirectByteCountEXT{}; + PFN_vkCmdDrawMultiEXT vkCmdDrawMultiEXT{}; + PFN_vkCmdDrawMultiIndexedEXT vkCmdDrawMultiIndexedEXT{}; PFN_vkCmdEndConditionalRenderingEXT vkCmdEndConditionalRenderingEXT{}; PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; PFN_vkCmdEndQuery vkCmdEndQuery{}; @@ -1239,6 +1241,19 @@ public: counter_buffer_offset, counter_offset, stride); } + void DrawMultiEXT(u32 draw_count, const VkMultiDrawInfoEXT* vertex_info, + u32 instance_count, u32 first_instance, u32 stride) const noexcept { + dld->vkCmdDrawMultiEXT(handle, draw_count, vertex_info, instance_count, first_instance, + stride); + } + + void DrawMultiIndexedEXT(u32 draw_count, const VkMultiDrawIndexedInfoEXT* index_info, + u32 instance_count, u32 first_instance, u32 stride, + const int32_t* vertex_offset) const noexcept { + dld->vkCmdDrawMultiIndexedEXT(handle, draw_count, index_info, instance_count, + first_instance, stride, vertex_offset); + } + void ClearAttachments(Span attachments, Span rects) const noexcept { dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),