Halide  20.0.0
Halide compiler and libraries
vulkan_context.h
Go to the documentation of this file.
1 #ifndef HALIDE_RUNTIME_VULKAN_CONTEXT_H
2 #define HALIDE_RUNTIME_VULKAN_CONTEXT_H
3 
4 #include "printer.h"
5 #include "runtime_internal.h"
6 #include "scoped_spin_lock.h"
7 
8 #include "vulkan_extensions.h"
9 #include "vulkan_internal.h"
10 #include "vulkan_memory.h"
11 
12 // --------------------------------------------------------------------------
13 
14 namespace Halide {
15 namespace Runtime {
16 namespace Internal {
17 namespace Vulkan {
18 
19 // --------------------------------------------------------------------------
20 
21 // Vulkan Memory allocator for host-device allocations
22 halide_vulkan_memory_allocator *WEAK cached_allocator = nullptr;
23 
24 // Cached instance related handles for device resources
25 VkInstance WEAK cached_instance = VK_NULL_HANDLE;
26 VkDevice WEAK cached_device = VK_NULL_HANDLE;
27 VkQueue WEAK cached_queue = VK_NULL_HANDLE;
28 VkPhysicalDevice WEAK cached_physical_device = VK_NULL_HANDLE;
30 VkDebugUtilsMessengerEXT WEAK cached_messenger = VK_NULL_HANDLE;
31 
32 // A Vulkan context/queue/synchronization lock defined in this module with weak linkage
34 
35 // --------------------------------------------------------------------------
36 
37 // Helper object to acquire and release the Vulkan context.
39  void *user_context;
40 
41 public:
43  VkInstance instance = VK_NULL_HANDLE;
44  VkDevice device = VK_NULL_HANDLE;
45  VkPhysicalDevice physical_device = VK_NULL_HANDLE;
46  VkQueue queue = VK_NULL_HANDLE;
47  uint32_t queue_family_index = 0; // used for operations requiring queue family
48  VkDebugUtilsMessengerEXT messenger = VK_NULL_HANDLE;
50 
51  HALIDE_ALWAYS_INLINE explicit VulkanContext(void *user_context)
53 
55  reinterpret_cast<halide_vulkan_memory_allocator **>(&allocator),
57  if (result != halide_error_code_success) {
60  }
62  halide_debug_assert(user_context, instance != VK_NULL_HANDLE);
63  halide_debug_assert(user_context, device != VK_NULL_HANDLE);
64  halide_debug_assert(user_context, queue != VK_NULL_HANDLE);
66  }
67 
70  }
71 
72  // For now, this is always nullptr
73  HALIDE_ALWAYS_INLINE const VkAllocationCallbacks *allocation_callbacks() {
74  return nullptr;
75  }
76 };
77 
78 // --------------------------------------------------------------------------
79 
80 namespace {
81 
82 int vk_find_compute_capability(void *user_context, int *major, int *minor) {
83  debug(user_context) << " vk_find_compute_capability (user_context: " << user_context << ")\n";
84 
85  VkInstance instance = VK_NULL_HANDLE;
86  VkDevice device = VK_NULL_HANDLE;
87  VkPhysicalDevice physical_device = VK_NULL_HANDLE;
88  uint32_t queue_family_index = 0;
89 
90  if (!lib_vulkan) {
91  // If the Vulkan loader can't be found, we want to return compute
92  // capability of (0, 0) ... this is not considered an error. So we
93  // should be very careful about looking for Vulkan without tripping
94  // any errors in the rest of this runtime.
95  void *sym = halide_vulkan_get_symbol(user_context, "vkCreateInstance");
96  if (!sym) {
97  *major = *minor = 0;
99  }
100  }
101 
102  if (vkCreateInstance == nullptr) {
104  if (vkCreateInstance == nullptr) {
105  debug(user_context) << " no valid vulkan loader library was found ...\n";
106  *major = *minor = 0;
108  }
109  }
110 
111  StringTable requested_layers;
112  vk_get_requested_layers(user_context, requested_layers);
113 
114  // Attempt to find a suitable instance that can support the requested layers
115  const VkAllocationCallbacks *alloc_callbacks = halide_vulkan_get_allocation_callbacks(user_context);
116  int status = vk_create_instance(user_context, requested_layers, &instance, alloc_callbacks);
117  if (status != halide_error_code_success) {
118  debug(user_context) << " no valid vulkan runtime was found ...\n";
119  *major = *minor = 0;
120  return 0;
121  }
122 
123  status = vk_select_device_for_context(user_context, &instance, &device, &physical_device, &queue_family_index);
124  if (status != halide_error_code_success) {
125  debug(user_context) << " no valid vulkan device was found ...\n";
126  *major = *minor = 0;
127  return 0;
128  }
129 
130  VkPhysicalDeviceProperties device_properties = {0};
131  debug(user_context) << " querying for device properties ...\n";
132  vkGetPhysicalDeviceProperties(physical_device, &device_properties);
133  *major = VK_API_VERSION_MAJOR(device_properties.apiVersion);
134  *minor = VK_API_VERSION_MINOR(device_properties.apiVersion);
135  debug(user_context) << " found device compute capability v" << *major << "." << *minor << " ...\n";
136 
137  vk_destroy_instance(user_context, instance, alloc_callbacks);
138  return 0;
139 }
140 
141 // Initializes the instance (used by the default vk_create_context)
142 int vk_create_instance(void *user_context, const StringTable &requested_layers, VkInstance *instance, const VkAllocationCallbacks *alloc_callbacks) {
143  debug(user_context) << " vk_create_instance (user_context: " << user_context << ")\n";
144 
145  StringTable required_instance_extensions;
146  vk_get_required_instance_extensions(user_context, required_instance_extensions);
147 
148  StringTable optional_instance_extensions;
149  vk_get_optional_instance_extensions(user_context, optional_instance_extensions);
150 
151  StringTable supported_instance_extensions;
152  vk_get_supported_instance_extensions(user_context, supported_instance_extensions);
153 
154  bool valid_instance = vk_validate_required_extension_support(user_context, required_instance_extensions, supported_instance_extensions);
155  halide_abort_if_false(user_context, valid_instance);
156 
157  debug(user_context) << " found " << (uint32_t)required_instance_extensions.size() << " required extensions for instance!\n";
158  for (int n = 0; n < (int)required_instance_extensions.size(); ++n) {
159  debug(user_context) << " extension: " << required_instance_extensions[n] << "\n";
160  }
161 
162  // enable all available optional extensions
163  debug(user_context) << " checking for " << (uint32_t)optional_instance_extensions.size() << " optional extensions for instance ...\n";
164  for (uint32_t n = 0; n < optional_instance_extensions.size(); ++n) {
165  if (supported_instance_extensions.contains(optional_instance_extensions[n])) {
166  debug(user_context) << " optional extension: " << optional_instance_extensions[n] << "\n";
167  required_instance_extensions.append(user_context, optional_instance_extensions[n]);
168  }
169  }
170 
171  // If we're running under Molten VK, we must enable the portability extension and create flags
172  // to allow non-physical devices that are emulated to appear in the device list.
173  uint32_t create_flags = 0;
174  if (supported_instance_extensions.contains("VK_KHR_portability_enumeration") &&
175  supported_instance_extensions.contains("VK_MVK_macos_surface")) {
176  create_flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
177  required_instance_extensions.append(user_context, "VK_KHR_portability_enumeration");
178  }
179 
180  VkApplicationInfo app_info = {
181  VK_STRUCTURE_TYPE_APPLICATION_INFO, // struct type
182  nullptr, // Next
183  "Runtime", // application name
184  VK_MAKE_API_VERSION(0, 1, 0, 0), // app version
185  "Halide", // engine name
186  VK_MAKE_API_VERSION(0, HALIDE_VERSION_MAJOR, HALIDE_VERSION_MINOR, HALIDE_VERSION_PATCH), // engine version
187  VK_API_VERSION_1_3}; // FIXME: only use the minimum capability necessary
188 
189  VkInstanceCreateInfo create_info = {
190  VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
191  nullptr, // Next
192  create_flags, // Flags
193  &app_info, // ApplicationInfo
194  (uint32_t)requested_layers.size(), requested_layers.data(), // Layers
195  (uint32_t)required_instance_extensions.size(), required_instance_extensions.data() // Extensions
196  };
197 
198  VkResult result = vkCreateInstance(&create_info, alloc_callbacks, instance);
199  if (result != VK_SUCCESS) {
200  debug(user_context) << "Vulkan: vkCreateInstance failed with return code: " << vk_get_error_name(result) << "\n";
202  }
203 
205  if (vkCreateDevice == nullptr) {
206  error(user_context) << "Vulkan: Failed to resolve instance API methods to create device!\n";
208  }
209 
211 }
212 
213 int vk_destroy_instance(void *user_context, VkInstance instance, const VkAllocationCallbacks *alloc_callbacks) {
214  debug(user_context) << " vk_destroy_instance (user_context: " << user_context << ", device: " << (void *)instance << ", alloc_callbacks: " << (void *)alloc_callbacks << ")\n";
215  vkDestroyInstance(instance, alloc_callbacks);
218 }
219 
220 int vk_select_device_for_context(void *user_context,
221  VkInstance *instance, VkDevice *device,
222  VkPhysicalDevice *physical_device,
223  uint32_t *queue_family_index) {
224  // query for the number of physical devices available in this instance
225  uint32_t device_count = 0;
226  VkResult result = vkEnumeratePhysicalDevices(*instance, &device_count, nullptr);
227  if ((result != VK_SUCCESS) && (result != VK_INCOMPLETE)) {
228  debug(user_context) << "Vulkan: vkEnumeratePhysicalDevices failed with return code: " << vk_get_error_name(result) << "\n";
230  }
231  if (device_count == 0) {
232  debug(user_context) << "Vulkan: No devices found.\n";
234  }
235 
236  // allocate enough storage for the physical device query results
237  BlockStorage::Config device_query_storage_config;
238  device_query_storage_config.entry_size = sizeof(VkPhysicalDevice);
239  BlockStorage device_query_storage(user_context, device_query_storage_config);
240  device_query_storage.resize(user_context, device_count);
241 
242  VkPhysicalDevice chosen_device = VK_NULL_HANDLE;
243  VkPhysicalDevice *avail_devices = (VkPhysicalDevice *)(device_query_storage.data());
244  if (avail_devices == nullptr) {
245  debug(user_context) << "Vulkan: Out of system memory!\n";
247  }
248  result = vkEnumeratePhysicalDevices(*instance, &device_count, avail_devices);
249  if ((result != VK_SUCCESS) && (result != VK_INCOMPLETE)) {
250  debug(user_context) << "Vulkan: vkEnumeratePhysicalDevices failed with return code: " << vk_get_error_name(result) << "\n";
252  }
253 
254  // get the configurable device type to search for (e.g. 'cpu', 'gpu', 'integrated-gpu', 'discrete-gpu', ...)
255  const char *dev_type = halide_vulkan_get_device_type(user_context);
256 
257  // try to find a matching device that supports compute.
258  uint32_t queue_family = 0;
259  for (uint32_t i = 0; (chosen_device == VK_NULL_HANDLE) && (i < device_count); i++) {
260  VkPhysicalDeviceProperties properties;
261  vkGetPhysicalDeviceProperties(avail_devices[i], &properties);
262  debug(user_context) << "Vulkan: Checking device #" << i << "='" << properties.deviceName << "'\n";
263 
264  int matching_device = 0;
265  if ((dev_type != nullptr) && (*dev_type != '\0')) {
266  if (strstr(dev_type, "cpu") && (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU)) {
267  matching_device = 1;
268  } else if (strstr(dev_type, "integrated-gpu") && ((properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU))) {
269  matching_device = 1;
270  } else if (strstr(dev_type, "discrete-gpu") && ((properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU))) {
271  matching_device = 1;
272  } else if (strstr(dev_type, "virtual-gpu") && (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)) {
273  matching_device = 1;
274  } else if (strstr(dev_type, "gpu") && ((properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) || (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU))) {
275  matching_device = 1;
276  }
277  } else {
278  // use a non-virtual gpu device by default
279  if ((properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) ||
280  (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)) {
281  matching_device = 1;
282  }
283  }
284 
285  if (matching_device) {
286  // get the number of supported queues for this physical device
287  uint32_t queue_properties_count = 0;
288  vkGetPhysicalDeviceQueueFamilyProperties(avail_devices[i], &queue_properties_count, nullptr);
289  if (queue_properties_count < 1) {
290  continue;
291  }
292 
293  // allocate enough storage for the queue properties query results
294  BlockStorage::Config queue_properties_storage_config;
295  queue_properties_storage_config.entry_size = sizeof(VkPhysicalDevice);
296  BlockStorage queue_properties_storage(user_context, queue_properties_storage_config);
297  queue_properties_storage.resize(user_context, queue_properties_count);
298 
299  VkQueueFamilyProperties *queue_properties = (VkQueueFamilyProperties *)(queue_properties_storage.data());
300  vkGetPhysicalDeviceQueueFamilyProperties(avail_devices[i], &queue_properties_count, queue_properties);
301  for (uint32_t j = 0; (chosen_device == nullptr) && (j < queue_properties_count); j++) {
302  if (queue_properties[j].queueCount > 0 &&
303  queue_properties[j].queueFlags & VK_QUEUE_COMPUTE_BIT) {
304  chosen_device = avail_devices[i];
305  queue_family = j;
306 
307  debug(user_context) << "Vulkan: Found matching compute device '" << properties.deviceName << "'\n";
308  }
309  }
310  }
311  }
312  // If nothing, just try the first one for now.
313  if (chosen_device == nullptr) {
314  queue_family = 0;
315  chosen_device = avail_devices[0];
316  VkPhysicalDeviceProperties properties;
317  vkGetPhysicalDeviceProperties(chosen_device, &properties);
318  debug(user_context) << "Vulkan: Defaulting to first compute device '" << properties.deviceName << "'\n";
319  }
320 
321  *queue_family_index = queue_family;
322  *physical_device = chosen_device;
324 }
325 
326 int vk_create_device(void *user_context, const StringTable &requested_layers, VkInstance *instance, VkDevice *device, VkQueue *queue,
327  VkPhysicalDevice *physical_device, uint32_t *queue_family_index, const VkAllocationCallbacks *alloc_callbacks) {
328  debug(user_context) << " vk_create_device (user_context=" << user_context << ")\n";
329 
330  debug(user_context) << " checking for required device extensions ...\n";
331  StringTable required_device_extensions;
332  vk_get_required_device_extensions(user_context, required_device_extensions);
333 
334  debug(user_context) << " checking for optional device extensions ...\n";
335  StringTable optional_device_extensions;
336  vk_get_optional_device_extensions(user_context, optional_device_extensions);
337 
338  debug(user_context) << " validating supported device extensions ...\n";
339  StringTable supported_device_extensions;
340  vk_get_supported_device_extensions(user_context, *physical_device, supported_device_extensions);
341 
342  bool valid_device = vk_validate_required_extension_support(user_context, required_device_extensions, supported_device_extensions);
343  if (!valid_device) {
344  debug(user_context) << "Vulkan: Unable to validate required extension support!\n";
346  }
347 
348  debug(user_context) << " found " << (uint32_t)required_device_extensions.size() << " required extensions for device!\n";
349  for (uint32_t n = 0; n < required_device_extensions.size(); ++n) {
350  debug(user_context) << " required extension: " << required_device_extensions[n] << "\n";
351  }
352 
353  // enable all available optional extensions
354  debug(user_context) << " checking for " << (uint32_t)optional_device_extensions.size() << " optional extensions for device ...\n";
355  for (uint32_t n = 0; n < optional_device_extensions.size(); ++n) {
356  if (supported_device_extensions.contains(optional_device_extensions[n])) {
357  debug(user_context) << " optional extension: " << optional_device_extensions[n] << "\n";
358  required_device_extensions.append(user_context, optional_device_extensions[n]);
359  }
360  }
361 
362  float queue_priority = 1.0f;
363  VkDeviceQueueCreateInfo device_queue_create_info = {
364  VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
365  nullptr, // Next
366  0, // Flags
367  *queue_family_index,
368  1,
369  &queue_priority,
370  };
371 
372  // Get the API version to determine what device features are valid to search for
373  VkPhysicalDeviceProperties device_properties = {0};
374  debug(user_context) << " querying for device properties ...\n";
375  vkGetPhysicalDeviceProperties(*physical_device, &device_properties);
376  uint32_t major_version = VK_API_VERSION_MAJOR(device_properties.apiVersion);
377  uint32_t minor_version = VK_API_VERSION_MINOR(device_properties.apiVersion);
378  bool has_capability_v11 = (major_version >= 1) && (minor_version >= 1); // supports >= v1.1
379  bool has_capability_v12 = (major_version >= 1) && (minor_version >= 2); // supports >= v1.2
380  debug(user_context) << " found device compute capability v" << major_version << "." << minor_version << " ...\n";
381 
382  // Get the device features so that all supported features are enabled when device is created
383  VkPhysicalDeviceFeatures device_features = {};
384  void *extended_features_ptr = nullptr;
385  void *standard_features_ptr = nullptr;
386 
387  debug(user_context) << " querying for device features...\n";
388  vkGetPhysicalDeviceFeatures(*physical_device, &device_features);
389  debug(user_context) << " shader float64 support: " << (device_features.shaderFloat64 ? "true" : "false") << "...\n";
390  debug(user_context) << " shader int64 support: " << (device_features.shaderInt64 ? "true" : "false") << "...\n";
391  debug(user_context) << " shader int16 support: " << (device_features.shaderInt16 ? "true" : "false") << "...\n";
392 
393  // assemble the chain of features to query, but only add the ones that exist in the API version
394 
395  // note: requires v1.2+
396  VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_f16_i8_ext = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR,
397  nullptr, VK_FALSE, VK_FALSE};
398 
399  // note: requires v1.2+
400  VkPhysicalDevice8BitStorageFeaturesKHR storage_8bit_ext = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR,
401  &shader_f16_i8_ext, VK_FALSE, VK_FALSE, VK_FALSE};
402 
403  // note: requires v1.1+
404  VkPhysicalDevice16BitStorageFeaturesKHR storage_16bit_ext = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
405  (has_capability_v12 ? &storage_8bit_ext : nullptr),
406  VK_FALSE, VK_FALSE, VK_FALSE, VK_FALSE};
407 
408  VkPhysicalDeviceFeatures2KHR device_features_ext = {
409  VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
410  &storage_16bit_ext, device_features};
411 
412  // Look for extended device feature query method (KHR was removed when it was adopted into v1.1+)
413  PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR = (PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(*instance, "vkGetPhysicalDeviceFeatures2KHR"); // v1.0+
414  if (!vkGetPhysicalDeviceFeatures2KHR) {
415  vkGetPhysicalDeviceFeatures2KHR = (PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(*instance, "vkGetPhysicalDeviceFeatures2");
416  }
417 
418  // If the instance runtime supports querying extended device features, request them
419  if (vkGetPhysicalDeviceFeatures2KHR && has_capability_v11) {
420 
421  debug(user_context) << " querying for extended device features...\n";
422  vkGetPhysicalDeviceFeatures2KHR(*physical_device, &device_features_ext);
423  debug(user_context) << " shader int8 support: " << (shader_f16_i8_ext.shaderInt8 ? "true" : "false") << "...\n";
424  debug(user_context) << " shader float16 support: " << (shader_f16_i8_ext.shaderFloat16 ? "true" : "false") << "...\n";
425  if (has_capability_v12) {
426  debug(user_context) << " storage buffer 8bit access support: " << (storage_8bit_ext.storageBuffer8BitAccess ? "true" : "false") << "...\n";
427  debug(user_context) << " storage buffer 16bit access support: " << (storage_16bit_ext.storageBuffer16BitAccess ? "true" : "false") << "...\n";
428  }
429  extended_features_ptr = (void *)(&device_features_ext); // pass extended features (which also contains the standard features)
430  } else {
431  standard_features_ptr = &device_features; // pass v1.0 standard features
432  }
433 
434  VkDeviceCreateInfo device_create_info = {
435  VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
436  extended_features_ptr, // Extended struct ptr (used here for requesting chain of extended features)
437  0, // Flags
438  1, // Count of queues to create
439  &device_queue_create_info,
440  (uint32_t)requested_layers.size(), requested_layers.data(), // Layers
441  (uint32_t)required_device_extensions.size(), required_device_extensions.data(), // Enabled extensions
442  (VkPhysicalDeviceFeatures *)standard_features_ptr, // Requested device features
443  };
444 
445  VkResult result = vkCreateDevice(*physical_device, &device_create_info, alloc_callbacks, device);
446  if (result != VK_SUCCESS) {
447  debug(user_context) << "Vulkan: vkCreateDevice failed with return code: " << vk_get_error_name(result) << "\n";
449  }
450 
452  if (vkAllocateMemory == nullptr) {
453  error(user_context) << "Vulkan: Failed to resolve device API methods for driver!\n";
455  }
456 
457  vkGetDeviceQueue(*device, *queue_family_index, 0, queue);
459 }
460 
461 int vk_destroy_device(void *user_context, VkDevice device, const VkAllocationCallbacks *alloc_callbacks) {
462  debug(user_context) << " vk_destroy_device (user_context: " << user_context << ", device: " << (void *)device << ", alloc_callbacks: " << (void *)alloc_callbacks << ")\n";
463  vkDestroyDevice(device, alloc_callbacks);
466 }
467 
468 // Initializes the context (used by the default implementation of halide_acquire_context)
469 int vk_create_context(void *user_context, VulkanMemoryAllocator **allocator,
470  VkInstance *instance, VkDevice *device, VkPhysicalDevice *physical_device,
471  VkQueue *queue, uint32_t *queue_family_index, VkDebugUtilsMessengerEXT *messenger) {
472 
473  debug(user_context) << " vk_create_context (user_context: " << user_context << ")\n";
474 
475  if (vkCreateInstance == nullptr) {
477  if (vkCreateInstance == nullptr) {
478  error(user_context) << "Vulkan: Failed to resolve loader library methods to create instance!\n";
480  }
481  }
482 
483  StringTable requested_layers;
484  uint32_t requested_layer_count = vk_get_requested_layers(user_context, requested_layers);
485  debug(user_context) << " requested " << requested_layer_count << " layers for instance!\n";
486  for (int n = 0; n < (int)requested_layer_count; ++n) {
487  debug(user_context) << " layer: " << requested_layers[n] << "\n";
488  }
489 
490  const VkAllocationCallbacks *alloc_callbacks = halide_vulkan_get_allocation_callbacks(user_context);
491  int error_code = vk_create_instance(user_context, requested_layers, instance, alloc_callbacks);
493  error(user_context) << "Vulkan: Failed to create instance for context!\n";
494  return error_code;
495  }
496 
497  error_code = vk_select_device_for_context(user_context, instance, device, physical_device, queue_family_index);
499  error(user_context) << "Vulkan: Failed to select device for context!\n";
500  return error_code;
501  }
502 
503  error_code = vk_create_device(user_context, requested_layers, instance, device, queue, physical_device, queue_family_index, alloc_callbacks);
505  error(user_context) << "Vulkan: Failed to create device for context!\n";
506  return error_code;
507  }
508 
509  *allocator = vk_create_memory_allocator(user_context, *device, *physical_device, alloc_callbacks);
510  if (*allocator == nullptr) {
511  error(user_context) << "Vulkan: Failed to create memory allocator for device!\n";
513  }
514 
515  vk_create_debug_utils_messenger(user_context, *instance, *allocator, messenger);
517 }
518 
519 // Destroys the context and all associated resources (used by halide_vulkan_device_release)
520 // NOTE: This should be called inside an acquire_context/release_context scope
521 int vk_destroy_context(void *user_context, VulkanMemoryAllocator *allocator,
522  VkInstance instance, VkDevice device, VkPhysicalDevice physical_device,
523  VkQueue queue, VkDebugUtilsMessengerEXT messenger) {
524 
525  debug(user_context)
526  << "vk_destroy_context (user_context: " << user_context << ")\n";
527 
528  if (vkCreateInstance == nullptr) {
530  }
531 
532  if (queue != VK_NULL_HANDLE) {
533  VkResult result = vkQueueWaitIdle(queue);
534  if (result != VK_SUCCESS) {
535  error(user_context) << "Vulkan: vkQueueWaitIdle returned " << vk_get_error_name(result) << "\n";
537  }
538  }
539  if (device != VK_NULL_HANDLE) {
540  VkResult result = vkDeviceWaitIdle(device);
541  if (result != VK_SUCCESS) {
542  error(user_context) << "Vulkan: vkDeviceWaitIdle returned " << vk_get_error_name(result) << "\n";
544  }
545  }
546 
547  if (allocator != nullptr) {
548  vk_destroy_shader_modules(user_context, allocator);
549  vk_destroy_memory_allocator(user_context, allocator);
550  vk_destroy_debug_utils_messenger(user_context, instance, allocator, messenger);
551  }
552 
553  const VkAllocationCallbacks *alloc_callbacks = halide_vulkan_get_allocation_callbacks(user_context);
554  if (device != VK_NULL_HANDLE) {
555  vk_destroy_device(user_context, device, alloc_callbacks);
556  }
557  if (instance != VK_NULL_HANDLE) {
558  vk_destroy_instance(user_context, instance, alloc_callbacks);
559  }
561 }
562 
563 // --------------------------------------------------------------------------
564 
565 VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_utils_messenger_callback(
566  VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
567  VkDebugUtilsMessageTypeFlagsEXT message_type,
568  const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
569  void *user_data) {
570  if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
571  print(user_data) << "Vulkan [WARNING]: (user_context=" << user_data << ", "
572  << "id=" << callback_data->messageIdNumber << ", "
573  << "name:" << callback_data->pMessageIdName << ") "
574  << callback_data->pMessage << "\n";
575  } else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
576  print(user_data) << "Vulkan [ERROR]: (user_context=" << user_data << ", "
577  << "id=" << callback_data->messageIdNumber << ", "
578  << "name:" << callback_data->pMessageIdName << ") "
579  << callback_data->pMessage << "\n";
580  } else {
581 
582  debug(user_data) << "Vulkan [DEBUG]: (user_context=" << user_data << ", "
583  << "id=" << callback_data->messageIdNumber << ", "
584  << "name:" << callback_data->pMessageIdName << ") "
585  << callback_data->pMessage << "\n";
586  }
587  return VK_FALSE;
588 }
589 
590 VkResult vk_create_debug_utils_messenger(void *user_context, VkInstance instance, VulkanMemoryAllocator *allocator, VkDebugUtilsMessengerEXT *messenger) {
591  PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
592  if (func != nullptr) {
593  debug(user_context) << "Vulkan: Registering Debug Utils Messenger ... \n";
594  VkDebugUtilsMessengerCreateInfoEXT create_info{};
595  create_info.flags = 0;
596  create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
597  create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
598  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;
599  create_info.pfnUserCallback = vk_debug_utils_messenger_callback;
600  create_info.pUserData = user_context;
601  return func(instance, &create_info, allocator->callbacks(), messenger);
602  } else {
603  debug(user_context) << "Vulkan: Debug Utils Messenger extension unavailable!\n";
604  return VK_ERROR_EXTENSION_NOT_PRESENT;
605  }
606 }
607 
608 void vk_destroy_debug_utils_messenger(void *user_context, VkInstance instance, VulkanMemoryAllocator *allocator, VkDebugUtilsMessengerEXT messenger) {
609  PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
610  if (func != nullptr) {
611  debug(user_context) << "Vulkan: Destroying Debug Utils Messenger ...\n";
612  func(instance, messenger, allocator->callbacks());
613  }
614 }
615 
616 // --------------------------------------------------------------------------
617 
618 } // namespace
619 } // namespace Vulkan
620 } // namespace Internal
621 } // namespace Runtime
622 } // namespace Halide
623 
624 #endif /// HALIDE_RUNTIME_VULKAN_CONTEXT_H
#define HALIDE_VERSION_MINOR
Definition: HalideRuntime.h:27
#define HALIDE_VERSION_PATCH
Definition: HalideRuntime.h:28
#define HALIDE_VERSION_MAJOR
Definition: HalideRuntime.h:26
int halide_error_no_device_interface(void *user_context)
halide_error_code_t
The error codes that may be returned by a Halide pipeline.
@ halide_error_code_symbol_not_found
A runtime symbol could not be loaded.
@ halide_error_code_device_interface_no_device
Buffer has a non-null device_interface but device is 0, which violates a Halide invariant.
@ halide_error_code_generic_error
An uncategorized error occurred.
@ halide_error_code_success
There was no error.
@ halide_error_code_out_of_memory
A call to halide_malloc returned NULL.
#define HALIDE_ALWAYS_INLINE
Definition: HalideRuntime.h:49
const char * halide_vulkan_get_device_type(void *user_context)
const struct VkAllocationCallbacks * halide_vulkan_get_allocation_callbacks(void *user_context)
int halide_vulkan_acquire_context(void *user_context, struct halide_vulkan_memory_allocator **allocator, VkInstance *instance, VkDevice *device, VkPhysicalDevice *physical_device, VkQueue *queue, uint32_t *queue_family_index, VkDebugUtilsMessengerEXT *messenger, bool create=true)
int halide_vulkan_release_context(void *user_context, VkInstance instance, VkDevice device, VkQueue queue, VkDebugUtilsMessengerEXT messenger)
void append(void *user_context, const char *str, size_t length=0)
Definition: string_table.h:145
bool contains(const char *str) const
Definition: string_table.h:191
HALIDE_ALWAYS_INLINE VulkanContext(void *user_context)
HALIDE_ALWAYS_INLINE const VkAllocationCallbacks * allocation_callbacks()
Vulkan Memory Allocator class interface for managing large memory requests stored as contiguous block...
Definition: vulkan_memory.h:42
const VkAllocationCallbacks * callbacks() const
Definition: vulkan_memory.h:85
void WEAK vk_unload_vulkan_device_functions(void *user_context)
void WEAK vk_unload_vulkan_instance_functions(void *user_context)
void WEAK vk_load_vulkan_loader_functions(void *user_context)
WEAK void * halide_vulkan_get_symbol(void *user_context, const char *name)
VkDebugUtilsMessengerEXT WEAK cached_messenger
void WEAK vk_load_vulkan_device_functions(void *user_context, VkDevice device)
VkPhysicalDevice WEAK cached_physical_device
void WEAK vk_load_vulkan_instance_functions(void *user_context, VkInstance instance)
halide_vulkan_memory_allocator *WEAK cached_allocator
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
@ Internal
Not visible externally, similar to 'static' linkage in C.
Expr print(const std::vector< Expr > &values)
Create an Expr that prints out its value whenever it is evaluated.
#define halide_debug_assert(user_context, cond)
halide_debug_assert() is like halide_assert(), but only expands into a check when DEBUG_RUNTIME is de...
const char * strstr(const char *, const char *)
unsigned __INT32_TYPE__ uint32_t
#define halide_abort_if_false(user_context, cond)
#define WEAK
Cross-platform mutex.
int error_code
void * user_context
VulkanMemoryAllocator * allocator