From 7e503fa053651091a49790590c8abde88a6d849e Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Fri, 21 Feb 2025 12:48:22 +0100
Subject: [PATCH] [dxvk] Enable VK_EXT_multi_draw if supported

---
 src/dxvk/dxvk_adapter.cpp   | 26 ++++++++++++++++++++++++++
 src/dxvk/dxvk_cmdlist.h     | 28 ++++++++++++++++++++++++++--
 src/dxvk/dxvk_device_info.h |  4 +++-
 src/dxvk/dxvk_extensions.h  |  1 +
 src/vulkan/vulkan_loader.h  |  5 +++++
 5 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/src/dxvk/dxvk_adapter.cpp b/src/dxvk/dxvk_adapter.cpp
index 12ede57b0..5c07fc88d 100644
--- a/src/dxvk/dxvk_adapter.cpp
+++ b/src/dxvk/dxvk_adapter.cpp
@@ -400,6 +400,10 @@ namespace dxvk {
         m_deviceFeatures.extLineRasterization.smoothLines;
     }
 
+    // Enable multi-draw for draw batching
+    enabledFeatures.extMultiDraw.multiDraw =
+      m_deviceFeatures.extMultiDraw.multiDraw;
+
     // Enable memory priority and pageable memory if supported
     // to improve driver-side memory management
     enabledFeatures.extMemoryPriority.memoryPriority =
@@ -600,6 +604,10 @@ namespace dxvk {
           enabledFeatures.extMemoryPriority = *reinterpret_cast<const VkPhysicalDeviceMemoryPriorityFeaturesEXT*>(f);
           break;
 
+        case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT:
+          enabledFeatures.extMultiDraw = *reinterpret_cast<const VkPhysicalDeviceMultiDrawFeaturesEXT*>(f);
+          break;
+
         case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT:
           enabledFeatures.extNonSeamlessCubeMap = *reinterpret_cast<const VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT*>(f);
           break;
@@ -791,6 +799,11 @@ namespace dxvk {
       m_deviceInfo.extLineRasterization.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.extLineRasterization);
     }
 
+    if (m_deviceExtensions.supports(VK_EXT_MULTI_DRAW_EXTENSION_NAME)) {
+      m_deviceInfo.extMultiDraw.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT;
+      m_deviceInfo.extMultiDraw.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.extMultiDraw);
+    }
+
     if (m_deviceExtensions.supports(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME)) {
       m_deviceInfo.extRobustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT;
       m_deviceInfo.extRobustness2.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.extRobustness2);
@@ -891,6 +904,11 @@ namespace dxvk {
       m_deviceFeatures.extMemoryPriority.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extMemoryPriority);
     }
 
+    if (m_deviceExtensions.supports(VK_EXT_MULTI_DRAW_EXTENSION_NAME)) {
+      m_deviceFeatures.extMultiDraw.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT;
+      m_deviceFeatures.extMultiDraw.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extMultiDraw);
+    }
+
     if (m_deviceExtensions.supports(VK_EXT_NON_SEAMLESS_CUBE_MAP_EXTENSION_NAME)) {
       m_deviceFeatures.extNonSeamlessCubeMap.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT;
       m_deviceFeatures.extNonSeamlessCubeMap.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extNonSeamlessCubeMap);
@@ -1017,6 +1035,7 @@ namespace dxvk {
       &devExtensions.extLineRasterization,
       &devExtensions.extMemoryBudget,
       &devExtensions.extMemoryPriority,
+      &devExtensions.extMultiDraw,
       &devExtensions.extNonSeamlessCubeMap,
       &devExtensions.extPageableDeviceLocalMemory,
       &devExtensions.extRobustness2,
@@ -1116,6 +1135,11 @@ namespace dxvk {
       enabledFeatures.extMemoryPriority.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extMemoryPriority);
     }
 
+    if (devExtensions.extMultiDraw) {
+      enabledFeatures.extMultiDraw.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT;
+      enabledFeatures.extMultiDraw.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extMultiDraw);
+    }
+
     if (devExtensions.extNonSeamlessCubeMap) {
       enabledFeatures.extNonSeamlessCubeMap.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT;
       enabledFeatures.extNonSeamlessCubeMap.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extNonSeamlessCubeMap);
@@ -1308,6 +1332,8 @@ namespace dxvk {
       "\n  extension supported                    : ", features.extMemoryBudget ? "1" : "0",
       "\n", VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME,
       "\n  memoryPriority                         : ", features.extMemoryPriority.memoryPriority ? "1" : "0",
+      "\n", VK_EXT_MULTI_DRAW_EXTENSION_NAME,
+      "\n  multiDraw                              : ", features.extMultiDraw.multiDraw ? "1" : "0",
       "\n", VK_EXT_NON_SEAMLESS_CUBE_MAP_EXTENSION_NAME,
       "\n  nonSeamlessCubeMap                     : ", features.extNonSeamlessCubeMap.nonSeamlessCubeMap ? "1" : "0",
       "\n", VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME,
diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h
index 9139d12fc..ac1a9cd93 100644
--- a/src/dxvk/dxvk_cmdlist.h
+++ b/src/dxvk/dxvk_cmdlist.h
@@ -722,6 +722,18 @@ namespace dxvk {
         vertexCount, instanceCount,
         firstVertex, firstInstance);
     }
+
+
+    void cmdDrawMulti(
+            uint32_t                drawCount,
+      const VkMultiDrawInfoEXT*     drawInfos,
+            uint32_t                instanceCount,
+            uint32_t                firstInstance) {
+      m_statCounters.addCtr(DxvkStatCounter::CmdDrawCalls, 1);
+
+      m_vkd->vkCmdDrawMultiEXT(getCmdBuffer(),
+        drawCount, drawInfos, instanceCount, firstInstance, sizeof(*drawInfos));
+    }
     
     
     void cmdDrawIndirect(
@@ -745,8 +757,8 @@ namespace dxvk {
             uint32_t                stride) {
       m_statCounters.addCtr(DxvkStatCounter::CmdDrawCalls, 1);
 
-      m_vkd->vkCmdDrawIndirectCount(getCmdBuffer(),
-        buffer, offset, countBuffer, countOffset, maxDrawCount, stride);
+      m_vkd->vkCmdDrawIndirectCount(getCmdBuffer(), buffer,
+        offset, countBuffer, countOffset, maxDrawCount, stride);
     }
     
     
@@ -765,6 +777,18 @@ namespace dxvk {
     }
     
     
+    void cmdDrawMultiIndexed(
+            uint32_t                drawCount,
+      const VkMultiDrawIndexedInfoEXT* drawInfos,
+            uint32_t                instanceCount,
+            uint32_t                firstInstance) {
+      m_statCounters.addCtr(DxvkStatCounter::CmdDrawCalls, 1);
+
+      m_vkd->vkCmdDrawMultiIndexedEXT(getCmdBuffer(), drawCount,
+        drawInfos, instanceCount, firstInstance, sizeof(*drawInfos), nullptr);
+    }
+
+
     void cmdDrawIndexedIndirect(
             VkBuffer                buffer,
             VkDeviceSize            offset,
diff --git a/src/dxvk/dxvk_device_info.h b/src/dxvk/dxvk_device_info.h
index b1e1eaf1d..9ab82ee6c 100644
--- a/src/dxvk/dxvk_device_info.h
+++ b/src/dxvk/dxvk_device_info.h
@@ -25,6 +25,7 @@ namespace dxvk {
     VkPhysicalDeviceExtendedDynamicState3PropertiesEXT        extExtendedDynamicState3;
     VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT      extGraphicsPipelineLibrary;
     VkPhysicalDeviceLineRasterizationPropertiesEXT            extLineRasterization;
+    VkPhysicalDeviceMultiDrawPropertiesEXT                    extMultiDraw;
     VkPhysicalDeviceRobustness2PropertiesEXT                  extRobustness2;
     VkPhysicalDeviceTransformFeedbackPropertiesEXT            extTransformFeedback;
     VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT       extVertexAttributeDivisor;
@@ -58,6 +59,7 @@ namespace dxvk {
     VkPhysicalDeviceLineRasterizationFeaturesEXT              extLineRasterization;
     VkBool32                                                  extMemoryBudget;
     VkPhysicalDeviceMemoryPriorityFeaturesEXT                 extMemoryPriority;
+    VkPhysicalDeviceMultiDrawFeaturesEXT                      extMultiDraw;
     VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT             extNonSeamlessCubeMap;
     VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT      extPageableDeviceLocalMemory;
     VkPhysicalDeviceRobustness2FeaturesEXT                    extRobustness2;
@@ -80,4 +82,4 @@ namespace dxvk {
     VkBool32                                                  khrWin32KeyedMutex;
   };
 
-}
\ No newline at end of file
+}
diff --git a/src/dxvk/dxvk_extensions.h b/src/dxvk/dxvk_extensions.h
index 4019ac5d2..76a94bff3 100644
--- a/src/dxvk/dxvk_extensions.h
+++ b/src/dxvk/dxvk_extensions.h
@@ -308,6 +308,7 @@ namespace dxvk {
     DxvkExt extLineRasterization              = { VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME,                 DxvkExtMode::Optional };
     DxvkExt extMemoryBudget                   = { VK_EXT_MEMORY_BUDGET_EXTENSION_NAME,                      DxvkExtMode::Passive  };
     DxvkExt extMemoryPriority                 = { VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME,                    DxvkExtMode::Optional };
+    DxvkExt extMultiDraw                      = { VK_EXT_MULTI_DRAW_EXTENSION_NAME,                         DxvkExtMode::Optional };
     DxvkExt extNonSeamlessCubeMap             = { VK_EXT_NON_SEAMLESS_CUBE_MAP_EXTENSION_NAME,              DxvkExtMode::Optional };
     DxvkExt extPageableDeviceLocalMemory      = { VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME,       DxvkExtMode::Optional };
     DxvkExt extRobustness2                    = { VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,                       DxvkExtMode::Required };
diff --git a/src/vulkan/vulkan_loader.h b/src/vulkan/vulkan_loader.h
index edc9d6df6..22ce278f5 100644
--- a/src/vulkan/vulkan_loader.h
+++ b/src/vulkan/vulkan_loader.h
@@ -405,6 +405,11 @@ namespace dxvk::vk {
     VULKAN_FN(vkSetDeviceMemoryPriorityEXT);
     #endif
 
+    #ifdef VK_EXT_multi_draw
+    VULKAN_FN(vkCmdDrawMultiEXT);
+    VULKAN_FN(vkCmdDrawMultiIndexedEXT);
+    #endif
+
     #ifdef VK_EXT_shader_module_identifier
     VULKAN_FN(vkGetShaderModuleCreateInfoIdentifierEXT);
     VULKAN_FN(vkGetShaderModuleIdentifierEXT);