Halide 22.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
22halide_vulkan_memory_allocator *WEAK cached_allocator = nullptr;
23
24// Cached instance related handles for device resources
25VkInstance WEAK cached_instance = VK_NULL_HANDLE;
26VkDevice WEAK cached_device = VK_NULL_HANDLE;
27VkQueue WEAK cached_queue = VK_NULL_HANDLE;
28VkPhysicalDevice WEAK cached_physical_device = VK_NULL_HANDLE;
29 uint32_t WEAK cached_queue_family_index = 0;
30VkDebugUtilsMessengerEXT WEAK cached_messenger = VK_NULL_HANDLE;
31
32// A Vulkan context/queue/synchronization lock defined in this module with weak linkage
33 WEAK halide_mutex thread_lock;
34
35// --------------------------------------------------------------------------
36
37// Helper object to acquire and release the Vulkan context.
38 class VulkanContext {
39 void *user_context;
40
41 public:
42 VulkanMemoryAllocator *allocator = nullptr;
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;
49 halide_error_code_t error = halide_error_code_success;
50
51 HALIDE_ALWAYS_INLINE explicit VulkanContext(void *user_context)
52 : user_context(user_context) {
53
54 int result = halide_vulkan_acquire_context(user_context,
55 reinterpret_cast<halide_vulkan_memory_allocator **>(&allocator),
56 &instance, &device, &physical_device, &queue, &queue_family_index, &messenger);
57 if (result != halide_error_code_success) {
58 error = halide_error_code_device_interface_no_device;
59 halide_error_no_device_interface(user_context);
60 }
61 halide_debug_assert(user_context, allocator != nullptr);
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);
65 halide_debug_assert(user_context, physical_device != VK_NULL_HANDLE);
66 }
67
68 HALIDE_ALWAYS_INLINE ~VulkanContext() {
69 halide_vulkan_release_context(user_context, instance, device, queue, messenger);
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;
98 return halide_error_code_success;
99 }
100 }
101
102 if (vkCreateInstance == nullptr) {
103 vk_load_vulkan_loader_functions(user_context);
104 if (vkCreateInstance == nullptr) {
105 debug(user_context) << " no valid vulkan loader library was found ...\n";
106 *major = *minor = 0;
107 return halide_error_code_success;
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";
201 return halide_error_code_device_interface_no_device;
202 }
203
204 vk_load_vulkan_instance_functions(user_context, *instance);
205 if (vkCreateDevice == nullptr) {
206 error(user_context) << "Vulkan: Failed to resolve instance API methods to create device!\n";
207 return halide_error_code_symbol_not_found;
208 }
209
210 return halide_error_code_success;
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);
216 vk_unload_vulkan_instance_functions(user_context);
217 return halide_error_code_success;
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";
229 return halide_error_code_device_interface_no_device;
230 }
231 if (device_count == 0) {
232 debug(user_context) << "Vulkan: No devices found.\n";
233 return halide_error_code_device_interface_no_device;
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";
246 return halide_error_code_out_of_memory;
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";
251 return halide_error_code_device_interface_no_device;
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;
323 return halide_error_code_success;
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";
345 return halide_error_code_device_interface_no_device;
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";
448 return halide_error_code_device_interface_no_device;
449 }
450
451 vk_load_vulkan_device_functions(user_context, *device);
452 if (vkAllocateMemory == nullptr) {
453 error(user_context) << "Vulkan: Failed to resolve device API methods for driver!\n";
454 return halide_error_code_symbol_not_found;
455 }
456
457 vkGetDeviceQueue(*device, *queue_family_index, 0, queue);
458 return halide_error_code_success;
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);
464 vk_unload_vulkan_device_functions(user_context);
465 return halide_error_code_success;
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) {
476 vk_load_vulkan_loader_functions(user_context);
477 if (vkCreateInstance == nullptr) {
478 error(user_context) << "Vulkan: Failed to resolve loader library methods to create instance!\n";
479 return halide_error_code_symbol_not_found;
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);
492 if (error_code != halide_error_code_success) {
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);
498 if (error_code != halide_error_code_success) {
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);
504 if (error_code != halide_error_code_success) {
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";
512 return halide_error_code_generic_error;
513 }
514
515 vk_create_debug_utils_messenger(user_context, *instance, *allocator, messenger);
516 return halide_error_code_success;
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) {
529 return halide_error_code_success;
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";
536 return halide_error_code_generic_error;
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";
543 return halide_error_code_generic_error;
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 }
560 return halide_error_code_success;
561}
562
563// --------------------------------------------------------------------------
564
565VKAPI_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
590VkResult 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
debug
#define debug(n)
For optional debugging during codegen, use the debug macro as follows:
Definition: Debug.h:52
HALIDE_VERSION_MINOR
#define HALIDE_VERSION_MINOR
Definition: HalideRuntime.h:27
HALIDE_VERSION_PATCH
#define HALIDE_VERSION_PATCH
Definition: HalideRuntime.h:28
HALIDE_VERSION_MAJOR
#define HALIDE_VERSION_MAJOR
Definition: HalideRuntime.h:26
halide_error_no_device_interface
int halide_error_no_device_interface(void *user_context)
halide_error_code_t
halide_error_code_t
The error codes that may be returned by a Halide pipeline.
Definition: HalideRuntime.h:1074
halide_error_code_symbol_not_found
@ halide_error_code_symbol_not_found
A runtime symbol could not be loaded.
Definition: HalideRuntime.h:1161
halide_error_code_device_interface_no_device
@ halide_error_code_device_interface_no_device
Buffer has a non-null device_interface but device is 0, which violates a Halide invariant.
Definition: HalideRuntime.h:1222
halide_error_code_generic_error
@ halide_error_code_generic_error
An uncategorized error occurred.
Definition: HalideRuntime.h:1079
halide_error_code_success
@ halide_error_code_success
There was no error.
Definition: HalideRuntime.h:1076
halide_error_code_out_of_memory
@ halide_error_code_out_of_memory
A call to halide_malloc returned NULL.
Definition: HalideRuntime.h:1119
HALIDE_ALWAYS_INLINE
#define HALIDE_ALWAYS_INLINE
Definition: HalideRuntime.h:49
halide_vulkan_acquire_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)
halide_vulkan_get_allocation_callbacks
const struct VkAllocationCallbacks * halide_vulkan_get_allocation_callbacks(void *user_context)
Definition: vulkan_memory.h:1501
halide_vulkan_release_context
int halide_vulkan_release_context(void *user_context, VkInstance instance, VkDevice device, VkQueue queue, VkDebugUtilsMessengerEXT messenger)
halide_vulkan_get_device_type
const char * halide_vulkan_get_device_type(void *user_context)
Definition: vulkan_extensions.h:311
Halide::Runtime::Internal::BlockStorage
Definition: block_storage.h:18
Halide::Runtime::Internal::StringTable
Definition: string_table.h:15
Halide::Runtime::Internal::StringTable::append
void append(void *user_context, const char *str, size_t length=0)
Definition: string_table.h:145
Halide::Runtime::Internal::StringTable::data
const char ** data() const
Definition: string_table.h:205
Halide::Runtime::Internal::StringTable::size
size_t size() const
Definition: string_table.h:54
Halide::Runtime::Internal::StringTable::contains
bool contains(const char *str) const
Definition: string_table.h:191
Halide::Runtime::Internal::Vulkan::VulkanContext
Definition: vulkan_context.h:38
Halide::Runtime::Internal::Vulkan::VulkanContext::device
VkDevice device
Definition: vulkan_context.h:44
Halide::Runtime::Internal::Vulkan::VulkanContext::allocator
VulkanMemoryAllocator * allocator
Definition: vulkan_context.h:42
Halide::Runtime::Internal::Vulkan::VulkanContext::queue_family_index
uint32_t queue_family_index
Definition: vulkan_context.h:47
Halide::Runtime::Internal::Vulkan::VulkanContext::VulkanContext
HALIDE_ALWAYS_INLINE VulkanContext(void *user_context)
Definition: vulkan_context.h:51
Halide::Runtime::Internal::Vulkan::VulkanContext::allocation_callbacks
HALIDE_ALWAYS_INLINE const VkAllocationCallbacks * allocation_callbacks()
Definition: vulkan_context.h:73
Halide::Runtime::Internal::Vulkan::VulkanContext::queue
VkQueue queue
Definition: vulkan_context.h:46
Halide::Runtime::Internal::Vulkan::VulkanContext::instance
VkInstance instance
Definition: vulkan_context.h:43
Halide::Runtime::Internal::Vulkan::VulkanContext::messenger
VkDebugUtilsMessengerEXT messenger
Definition: vulkan_context.h:48
Halide::Runtime::Internal::Vulkan::VulkanContext::physical_device
VkPhysicalDevice physical_device
Definition: vulkan_context.h:45
Halide::Runtime::Internal::Vulkan::VulkanContext::~VulkanContext
HALIDE_ALWAYS_INLINE ~VulkanContext()
Definition: vulkan_context.h:68
Halide::Runtime::Internal::Vulkan::VulkanContext::error
halide_error_code_t error
Definition: vulkan_context.h:49
Halide::Runtime::Internal::Vulkan::VulkanMemoryAllocator
Vulkan Memory Allocator class interface for managing large memory requests stored as contiguous block...
Definition: vulkan_memory.h:42
Halide::Runtime::Internal::Vulkan::VulkanMemoryAllocator::callbacks
const VkAllocationCallbacks * callbacks() const
Definition: vulkan_memory.h:85
Halide::Runtime::Internal::Vulkan::halide_vulkan_get_symbol
WEAK void * halide_vulkan_get_symbol(void *user_context, const char *name)
Definition: vulkan_interface.h:42
Halide::Runtime::Internal::Vulkan::vk_unload_vulkan_device_functions
void WEAK vk_unload_vulkan_device_functions(void *user_context)
Definition: vulkan_interface.h:124
Halide::Runtime::Internal::Vulkan::vk_unload_vulkan_instance_functions
void WEAK vk_unload_vulkan_instance_functions(void *user_context)
Definition: vulkan_interface.h:105
Halide::Runtime::Internal::Vulkan::thread_lock
WEAK halide_mutex thread_lock
Definition: vulkan_context.h:33
Halide::Runtime::Internal::Vulkan::cached_queue
VkQueue WEAK cached_queue
Definition: vulkan_context.h:27
Halide::Runtime::Internal::Vulkan::vk_load_vulkan_loader_functions
void WEAK vk_load_vulkan_loader_functions(void *user_context)
Definition: vulkan_interface.h:85
Halide::Runtime::Internal::Vulkan::cached_messenger
VkDebugUtilsMessengerEXT WEAK cached_messenger
Definition: vulkan_context.h:30
Halide::Runtime::Internal::Vulkan::vk_load_vulkan_device_functions
void WEAK vk_load_vulkan_device_functions(void *user_context, VkDevice device)
Definition: vulkan_interface.h:114
Halide::Runtime::Internal::Vulkan::cached_instance
VkInstance WEAK cached_instance
Definition: vulkan_context.h:25
Halide::Runtime::Internal::Vulkan::cached_queue_family_index
uint32_t WEAK cached_queue_family_index
Definition: vulkan_context.h:29
Halide::Runtime::Internal::Vulkan::cached_physical_device
VkPhysicalDevice WEAK cached_physical_device
Definition: vulkan_context.h:28
Halide::Runtime::Internal::Vulkan::vk_load_vulkan_instance_functions
void WEAK vk_load_vulkan_instance_functions(void *user_context, VkInstance instance)
Definition: vulkan_interface.h:95
Halide::Runtime::Internal::Vulkan::cached_allocator
halide_vulkan_memory_allocator *WEAK cached_allocator
Definition: vulkan_context.h:22
Halide::Runtime::Internal::Vulkan::lib_vulkan
WEAK void * lib_vulkan
Definition: vulkan_interface.h:40
Halide::Runtime::Internal::Vulkan::cached_device
VkDevice WEAK cached_device
Definition: vulkan_context.h:26
Halide
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
Definition: AbstractGenerator.h:19
Halide::LinkageType::Internal
@ Internal
Not visible externally, similar to 'static' linkage in C.
Halide::DeviceAPI::Vulkan
@ Vulkan
Halide::print
Expr print(const std::vector< Expr > &values)
Create an Expr that prints out its value whenever it is evaluated.
printer.h
runtime_internal.h
halide_debug_assert
#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...
Definition: runtime_internal.h:290
uint32_t
unsigned __INT32_TYPE__ uint32_t
Definition: runtime_internal.h:25
halide_abort_if_false
#define halide_abort_if_false(user_context, cond)
Definition: runtime_internal.h:271
strstr
const char * strstr(const char *, const char *)
WEAK
#define WEAK
Definition: runtime_internal.h:52
scoped_spin_lock.h
Halide::Runtime::Internal::BlockStorage::Config
Definition: block_storage.h:23
Halide::Runtime::Internal::BlockStorage::Config::entry_size
uint32_t entry_size
Definition: block_storage.h:24
halide_mutex
Cross-platform mutex.
Definition: HalideRuntime.h:191
vulkan_extensions.h
vulkan_internal.h
vulkan_memory.h
error_code
int error_code
Definition: vulkan_resources.h:182
user_context
void * user_context
Definition: vulkan_resources.h:178
allocator
VulkanMemoryAllocator * allocator
Definition: vulkan_resources.h:179