ComputeSystemVKImpl.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2025 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #ifdef JPH_USE_VK
  6. #include <Jolt/Compute/VK/ComputeSystemVKImpl.h>
  7. #include <Jolt/Core/QuickSort.h>
  8. JPH_NAMESPACE_BEGIN
  9. #ifdef JPH_DEBUG
  10. static VKAPI_ATTR VkBool32 VKAPI_CALL sVulkanDebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT inSeverity, [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT inType, const VkDebugUtilsMessengerCallbackDataEXT *inCallbackData, [[maybe_unused]] void *inUserData)
  11. {
  12. if (inSeverity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT))
  13. Trace("VK: %s", inCallbackData->pMessage);
  14. JPH_ASSERT((inSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) == 0);
  15. return VK_FALSE;
  16. }
  17. #endif // JPH_DEBUG
  18. ComputeSystemVKImpl::~ComputeSystemVKImpl()
  19. {
  20. ComputeSystemVK::Shutdown();
  21. if (mDevice != VK_NULL_HANDLE)
  22. vkDestroyDevice(mDevice, nullptr);
  23. #ifdef JPH_DEBUG
  24. PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)(void *)vkGetInstanceProcAddr(mInstance, "vkDestroyDebugUtilsMessengerEXT");
  25. if (mInstance != VK_NULL_HANDLE && mDebugMessenger != VK_NULL_HANDLE && vkDestroyDebugUtilsMessengerEXT != nullptr)
  26. vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, nullptr);
  27. #endif
  28. if (mInstance != VK_NULL_HANDLE)
  29. vkDestroyInstance(mInstance, nullptr);
  30. }
  31. bool ComputeSystemVKImpl::Initialize()
  32. {
  33. // Required instance extensions
  34. Array<const char *> required_instance_extensions;
  35. required_instance_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
  36. required_instance_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
  37. #ifdef JPH_PLATFORM_MACOS
  38. required_instance_extensions.push_back("VK_KHR_portability_enumeration");
  39. required_instance_extensions.push_back("VK_KHR_get_physical_device_properties2");
  40. #endif
  41. GetInstanceExtensions(required_instance_extensions);
  42. // Required device extensions
  43. Array<const char *> required_device_extensions;
  44. required_device_extensions.push_back(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
  45. #ifdef JPH_PLATFORM_MACOS
  46. required_device_extensions.push_back("VK_KHR_portability_subset"); // VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
  47. #endif
  48. GetDeviceExtensions(required_device_extensions);
  49. // Query supported instance extensions
  50. uint32 instance_extension_count = 0;
  51. if (VKFailed(vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr)))
  52. return false;
  53. Array<VkExtensionProperties> instance_extensions;
  54. instance_extensions.resize(instance_extension_count);
  55. if (VKFailed(vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, instance_extensions.data())))
  56. return false;
  57. // Query supported validation layers
  58. uint32 validation_layer_count;
  59. vkEnumerateInstanceLayerProperties(&validation_layer_count, nullptr);
  60. Array<VkLayerProperties> validation_layers(validation_layer_count);
  61. vkEnumerateInstanceLayerProperties(&validation_layer_count, validation_layers.data());
  62. VkApplicationInfo app_info = {};
  63. app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
  64. app_info.apiVersion = VK_API_VERSION_1_1;
  65. // Create Vulkan instance
  66. VkInstanceCreateInfo instance_create_info = {};
  67. instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
  68. #ifdef JPH_PLATFORM_MACOS
  69. instance_create_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
  70. #endif
  71. instance_create_info.pApplicationInfo = &app_info;
  72. #ifdef JPH_DEBUG
  73. // Enable validation layer if supported
  74. const char *desired_validation_layers[] = { "VK_LAYER_KHRONOS_validation" };
  75. for (const VkLayerProperties &p : validation_layers)
  76. if (strcmp(desired_validation_layers[0], p.layerName) == 0)
  77. {
  78. instance_create_info.enabledLayerCount = 1;
  79. instance_create_info.ppEnabledLayerNames = desired_validation_layers;
  80. break;
  81. }
  82. // Setup debug messenger callback if the extension is supported
  83. VkDebugUtilsMessengerCreateInfoEXT messenger_create_info = {};
  84. for (const VkExtensionProperties &ext : instance_extensions)
  85. if (strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, ext.extensionName) == 0)
  86. {
  87. messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
  88. messenger_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
  89. messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
  90. messenger_create_info.pfnUserCallback = sVulkanDebugCallback;
  91. instance_create_info.pNext = &messenger_create_info;
  92. required_instance_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
  93. break;
  94. }
  95. #endif
  96. instance_create_info.enabledExtensionCount = (uint32)required_instance_extensions.size();
  97. instance_create_info.ppEnabledExtensionNames = required_instance_extensions.data();
  98. if (VKFailed(vkCreateInstance(&instance_create_info, nullptr, &mInstance)))
  99. return false;
  100. #ifdef JPH_DEBUG
  101. // Finalize debug messenger callback
  102. PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)(std::uintptr_t)vkGetInstanceProcAddr(mInstance, "vkCreateDebugUtilsMessengerEXT");
  103. if (vkCreateDebugUtilsMessengerEXT != nullptr)
  104. if (VKFailed(vkCreateDebugUtilsMessengerEXT(mInstance, &messenger_create_info, nullptr, &mDebugMessenger)))
  105. return false;
  106. #endif
  107. // Notify that instance has been created
  108. OnInstanceCreated();
  109. // Select device
  110. uint32 device_count = 0;
  111. if (VKFailed(vkEnumeratePhysicalDevices(mInstance, &device_count, nullptr)))
  112. return false;
  113. Array<VkPhysicalDevice> devices;
  114. devices.resize(device_count);
  115. if (VKFailed(vkEnumeratePhysicalDevices(mInstance, &device_count, devices.data())))
  116. return false;
  117. struct Device
  118. {
  119. VkPhysicalDevice mPhysicalDevice;
  120. String mName;
  121. VkSurfaceFormatKHR mFormat;
  122. uint32 mGraphicsQueueIndex;
  123. uint32 mPresentQueueIndex;
  124. uint32 mComputeQueueIndex;
  125. int mScore;
  126. };
  127. Array<Device> available_devices;
  128. for (VkPhysicalDevice device : devices)
  129. {
  130. // Get device properties
  131. VkPhysicalDeviceProperties properties;
  132. vkGetPhysicalDeviceProperties(device, &properties);
  133. // Test if it is an appropriate type
  134. int score = 0;
  135. switch (properties.deviceType)
  136. {
  137. case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
  138. score = 30;
  139. break;
  140. case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
  141. score = 20;
  142. break;
  143. case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
  144. score = 10;
  145. break;
  146. case VK_PHYSICAL_DEVICE_TYPE_CPU:
  147. score = 5;
  148. break;
  149. case VK_PHYSICAL_DEVICE_TYPE_OTHER:
  150. case VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM:
  151. continue;
  152. }
  153. // Check if the device supports all our required extensions
  154. uint32 device_extension_count;
  155. vkEnumerateDeviceExtensionProperties(device, nullptr, &device_extension_count, nullptr);
  156. Array<VkExtensionProperties> available_extensions;
  157. available_extensions.resize(device_extension_count);
  158. vkEnumerateDeviceExtensionProperties(device, nullptr, &device_extension_count, available_extensions.data());
  159. int found_extensions = 0;
  160. for (const char *required_device_extension : required_device_extensions)
  161. for (const VkExtensionProperties &ext : available_extensions)
  162. if (strcmp(required_device_extension, ext.extensionName) == 0)
  163. {
  164. found_extensions++;
  165. break;
  166. }
  167. if (found_extensions != int(required_device_extensions.size()))
  168. continue;
  169. // Find the right queues
  170. uint32 queue_family_count = 0;
  171. vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, nullptr);
  172. Array<VkQueueFamilyProperties> queue_families;
  173. queue_families.resize(queue_family_count);
  174. vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data());
  175. uint32 graphics_queue = ~uint32(0);
  176. uint32 present_queue = ~uint32(0);
  177. uint32 compute_queue = ~uint32(0);
  178. for (uint32 i = 0; i < uint32(queue_families.size()); ++i)
  179. {
  180. if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
  181. {
  182. graphics_queue = i;
  183. if (queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
  184. compute_queue = i;
  185. }
  186. if (HasPresentSupport(device, i))
  187. present_queue = i;
  188. if (graphics_queue != ~uint32(0) && present_queue != ~uint32(0) && compute_queue != ~uint32(0))
  189. break;
  190. }
  191. if (graphics_queue == ~uint32(0) || present_queue == ~uint32(0) || compute_queue == ~uint32(0))
  192. continue;
  193. // Select surface format
  194. VkSurfaceFormatKHR selected_format = SelectFormat(device);
  195. if (selected_format.format == VK_FORMAT_UNDEFINED)
  196. continue;
  197. // Add the device
  198. available_devices.push_back({ device, properties.deviceName, selected_format, graphics_queue, present_queue, compute_queue, score });
  199. }
  200. if (available_devices.empty())
  201. return false;
  202. // Sort the devices by score
  203. QuickSort(available_devices.begin(), available_devices.end(), [](const Device &inLHS, const Device &inRHS) {
  204. return inLHS.mScore > inRHS.mScore;
  205. });
  206. const Device &selected_device = available_devices[0];
  207. // Create device
  208. float queue_priority = 1.0f;
  209. VkDeviceQueueCreateInfo queue_create_info[3] = {};
  210. for (size_t i = 0; i < std::size(queue_create_info); ++i)
  211. {
  212. queue_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
  213. queue_create_info[i].queueCount = 1;
  214. queue_create_info[i].pQueuePriorities = &queue_priority;
  215. }
  216. uint32 num_queues = 0;
  217. queue_create_info[num_queues++].queueFamilyIndex = selected_device.mGraphicsQueueIndex;
  218. for (uint32 i = 0; i < num_queues; ++i)
  219. if (queue_create_info[i].queueFamilyIndex != selected_device.mPresentQueueIndex)
  220. queue_create_info[num_queues++].queueFamilyIndex = selected_device.mPresentQueueIndex;
  221. for (uint32 i = 0; i < num_queues; ++i)
  222. if (queue_create_info[i].queueFamilyIndex != selected_device.mComputeQueueIndex)
  223. queue_create_info[num_queues++].queueFamilyIndex = selected_device.mComputeQueueIndex;
  224. VkPhysicalDeviceScalarBlockLayoutFeatures enable_scalar_block = {};
  225. enable_scalar_block.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES;
  226. enable_scalar_block.scalarBlockLayout = VK_TRUE;
  227. VkPhysicalDeviceFeatures2 enabled_features2 = {};
  228. enabled_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
  229. GetEnabledFeatures(enabled_features2);
  230. enable_scalar_block.pNext = enabled_features2.pNext;
  231. enabled_features2.pNext = &enable_scalar_block;
  232. VkDeviceCreateInfo device_create_info = {};
  233. device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
  234. device_create_info.queueCreateInfoCount = num_queues;
  235. device_create_info.pQueueCreateInfos = queue_create_info;
  236. device_create_info.enabledLayerCount = instance_create_info.enabledLayerCount;
  237. device_create_info.ppEnabledLayerNames = instance_create_info.ppEnabledLayerNames;
  238. device_create_info.enabledExtensionCount = uint32(required_device_extensions.size());
  239. device_create_info.ppEnabledExtensionNames = required_device_extensions.data();
  240. device_create_info.pNext = &enabled_features2;
  241. device_create_info.pEnabledFeatures = nullptr;
  242. VkDevice device = VK_NULL_HANDLE;
  243. if (VKFailed(vkCreateDevice(selected_device.mPhysicalDevice, &device_create_info, nullptr, &device)))
  244. return false;
  245. // Get the queues
  246. mGraphicsQueueIndex = selected_device.mGraphicsQueueIndex;
  247. mPresentQueueIndex = selected_device.mPresentQueueIndex;
  248. vkGetDeviceQueue(device, mGraphicsQueueIndex, 0, &mGraphicsQueue);
  249. vkGetDeviceQueue(device, mPresentQueueIndex, 0, &mPresentQueue);
  250. // Store selected format
  251. mSelectedFormat = selected_device.mFormat;
  252. // Initialize the compute system
  253. return ComputeSystemVK::Initialize(selected_device.mPhysicalDevice, device, selected_device.mComputeQueueIndex);
  254. }
  255. ComputeSystem *CreateComputeSystemVK()
  256. {
  257. ComputeSystemVKImpl *compute = new ComputeSystemVKImpl;
  258. if (compute->Initialize())
  259. return compute;
  260. delete compute;
  261. return nullptr;
  262. }
  263. JPH_NAMESPACE_END
  264. #endif // JPH_USE_VK