kiba-engine
instance.c
1 #include <kiba/gpu/vulkan/instance.h>
2 
3 #include <kiba/containers/array.h>
4 #include <kiba/core/log.h>
5 #include <kiba/core/memory.h>
6 #include <kiba/core/string.h>
7 #include <kiba/gpu/vulkan/allocator.h>
8 #include <kiba/gpu/vulkan/util.h>
9 
10 struct vk_instance_t vk_instance = { 0 };
11 
13  array_of(const char *) extensions;
14  array_of(const char *) layers;
15 };
16 
18  struct vk_instance_requirements req = {
19  .extensions = array_create(const char *, 5, &vk_alloc.kiba_alloc),
20  .layers = array_create(const char *, 4, &vk_alloc.kiba_alloc),
21  };
22  KB_ASSERT(req.extensions != KB_NULL, "instance extension array creation must be successful");
23  KB_ASSERT(req.layers != KB_NULL, "instance layer array creation must be successful");
24 
25  array_push(req.extensions, VK_KHR_SURFACE_EXTENSION_NAME);
26 #ifdef KB_LINUX
27  array_push(req.extensions, "VK_KHR_xcb_surface");
28 #endif
29 #ifdef KB_DEBUG_BUILD
30  array_push(req.extensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
31  array_push(req.layers, "VK_LAYER_KHRONOS_validation");
32 #endif
33  return req;
34 }
35 
36 b8 vk_verify_extension_availability(array_of(const char *) names);
37 b8 vk_verify_layer_availability(array_of(const char *) names);
38 
39 b8 vk_debug_messenger_create(void);
40 void vk_debug_messenger_destroy(void);
41 
42 b8 vk_instance_initialize(void) {
43  VkApplicationInfo app_info = {
44  .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
45  .pApplicationName = "kiba-vulkan-backend", // TODO pass that and other app info into backend creation
46  .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
47  .pEngineName = "kiba",
48  .engineVersion = VK_MAKE_VERSION(0, 0, 1),
49  .apiVersion = VK_API_VERSION_1_0,
50  };
51 
52  struct vk_instance_requirements requirements = vk_instance_requirements();
53 
54  if (!vk_verify_extension_availability(requirements.extensions)) {
55  KB_ERROR("not all required extensions available");
56  return false;
57  }
58 
59  if (!vk_verify_layer_availability(requirements.layers)) {
60  KB_ERROR("not all required layers available");
61  return false;
62  }
63 
64 #ifdef KB_DEBUG_BUILD
65  VkValidationFeatureEnableEXT enabled[] = {VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT};
66  VkValidationFeaturesEXT features = {
67  .sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
68  .disabledValidationFeatureCount = 0,
69  .enabledValidationFeatureCount = 1,
70  .pDisabledValidationFeatures = VK_NULL_HANDLE,
71  .pEnabledValidationFeatures = enabled,
72  };
73 #endif
74 
75  VkInstanceCreateInfo instance_info = {
76  .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
77  .pApplicationInfo = &app_info,
78  .enabledLayerCount = (u32) array_size(requirements.layers),
79  .ppEnabledLayerNames = requirements.layers,
80  .enabledExtensionCount = (u32) array_size(requirements.extensions),
81  .ppEnabledExtensionNames = requirements.extensions,
82 #ifdef KB_DEBUG_BUILD
83  .pNext = &features,
84 #endif
85  };
86 
87  VK_CALL_B8(vkCreateInstance(&instance_info, &vk_alloc.vulkan_callbacks, &vk_instance.raw));
88 
89 #ifdef KB_DEBUG_BUILD
90  if (!vk_debug_messenger_create()) {
91  KB_ERROR("could not create vulkan debug messenger");
92  return false;
93  }
94  vk_instance.setDebugUtilsObjectName =
95  (PFN_vkSetDebugUtilsObjectNameEXT) vkGetInstanceProcAddr(vk_instance.raw, "vkSetDebugUtilsObjectNameEXT");
96  vk_instance.cmdBeginDebugUtilsLabel =
97  (PFN_vkCmdBeginDebugUtilsLabelEXT) vkGetInstanceProcAddr(vk_instance.raw, "vkCmdBeginDebugUtilsLabelEXT");
98  vk_instance.cmdEndDebugUtilsLabel =
99  (PFN_vkCmdEndDebugUtilsLabelEXT) vkGetInstanceProcAddr(vk_instance.raw, "vkCmdEndDebugUtilsLabelEXT");
100  vk_instance.cmdInsertDebugUtilsLabel =
101  (PFN_vkCmdInsertDebugUtilsLabelEXT) vkGetInstanceProcAddr(vk_instance.raw, "vkCmdInsertDebugUtilsLabelEXT");
102 #endif
103 
104  return true;
105 }
106 
107 void vk_instance_shutdown(void) {
108  if (vk_instance.raw != VK_NULL_HANDLE) {
109 #ifdef KB_DEBUG_BUILD
110  vk_debug_messenger_destroy();
111 #endif
112  vkDestroyInstance(vk_instance.raw, &vk_alloc.vulkan_callbacks);
113  vk_instance.raw = VK_NULL_HANDLE;
114  } else {
115  KB_WARN("tried to destroy uninitialized vulkan instance");
116  }
117 }
118 
119 b8 vk_verify_extension_availability(array_of(const char *) names) {
120  u32 avail_extension_count = 0;
121  VK_CALL_B8(vkEnumerateInstanceExtensionProperties(0, &avail_extension_count, 0))
122  if (avail_extension_count < array_size(names)) {
123  KB_INFO("not all required extensions available");
124  return false;
125  }
126  array_of(VkExtensionProperties) available_extensions =
127  array_create(VkExtensionProperties, avail_extension_count, array_alloc(names));
128  array_resize(&available_extensions, avail_extension_count);
129  if (available_extensions == KB_NULL) {
130  KB_INFO("could not allocate enough memory for list of available extensions");
131  return false;
132  }
133  // NOTE: this leaks in case it is unsuccessful
134  VK_CALL_B8(vkEnumerateInstanceExtensionProperties(0, &avail_extension_count, available_extensions));
135 
136  b8 has_extensions = true;
137  array_for_each(const char *, name, names) {
138  b8 found = false;
139  for (u32 j = 0; j < avail_extension_count; ++j) {
140  if (string_equal(string_from_raw(*name), string_from_raw(available_extensions[j].extensionName))) {
141  found = true;
142  break;
143  }
144  }
145  if (!found) {
146  KB_ERROR("could not find required extension {raw_string}", *name);
147  has_extensions = false;
148  }
149  }
150  array_destroy(&available_extensions);
151  return has_extensions;
152 }
153 
154 b8 vk_verify_layer_availability(array_of(const char *) names) {
155  u32 avail_layer_count = 0;
156  VK_CALL_B8(vkEnumerateInstanceLayerProperties(&avail_layer_count, 0));
157  if (avail_layer_count < array_size(names)) {
158  KB_INFO("not all required extensions available");
159  return false;
160  }
161  array_of(VkLayerProperties) avail_layers = array_create(VkLayerProperties, avail_layer_count, array_alloc(names));
162  array_resize(&avail_layers, avail_layer_count);
163  if (avail_layers == KB_NULL) {
164  KB_INFO("could not allocate enough memory for list of available layers");
165  return false;
166  }
167 
168  // TODO: this leaks in case it is unsuccessful
169  VK_CALL_B8(vkEnumerateInstanceLayerProperties(&avail_layer_count, avail_layers));
170 
171  b8 has_layers = true;
172  array_for_each(const char *, name, names) {
173  b8 found = false;
174  for (u32 j = 0; j < avail_layer_count; ++j) {
175  if (string_equal(string_from_raw(*name), string_from_raw(avail_layers[j].layerName))) {
176  found = true;
177  break;
178  }
179  }
180  if (!found) {
181  KB_ERROR("could not find required validation layer {raw_string}", *name);
182  has_layers = false;
183  }
184  }
185  array_destroy(&avail_layers);
186  return has_layers;
187 }
188 
189 VkDebugUtilsMessengerEXT vk_debug_messenger = {0};
190 VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
191  VkDebugUtilsMessageTypeFlagsEXT message_types,
192  const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
193  void *user_data);
194 
195 b8 vk_debug_messenger_create(void) {
196  u32 log_severities =
197  /*VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |*/ VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
198  | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
199  u32 message_types = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
200  | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
201  VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {
202  .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
203  .messageSeverity = log_severities,
204  .messageType = message_types,
205  .pfnUserCallback = vk_debug_callback,
206  };
207  PFN_vkCreateDebugUtilsMessengerEXT messenger_add_func =
208  (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(vk_instance.raw, "vkCreateDebugUtilsMessengerEXT");
209  if (messenger_add_func == KB_NULL) {
210  KB_ERROR("could not create vulkan debug messenger");
211  return false;
212  }
213  VK_CALL_B8(
214  messenger_add_func(vk_instance.raw, &debug_create_info, &vk_alloc.vulkan_callbacks, &vk_debug_messenger));
215  return true;
216 }
217 
218 void vk_debug_messenger_destroy(void) {
219  PFN_vkDestroyDebugUtilsMessengerEXT messenger_remove_func =
220  (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(vk_instance.raw, "vkDestroyDebugUtilsMessengerEXT");
221  if (messenger_remove_func == KB_NULL) {
222  KB_ERROR("received invalid function to destroy debug messenger");
223  return;
224  }
225  messenger_remove_func(vk_instance.raw, vk_debug_messenger, &vk_alloc.vulkan_callbacks);
226 }
227 
228 VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
229  VkDebugUtilsMessageTypeFlagsEXT message_types,
230  const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
231  void *user_data) {
232  UNUSED(message_types);
233  UNUSED(user_data);
234  switch (message_severity) {
235  case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
236  KB_TRACE(callback_data->pMessage);
237  break;
238  case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
239  KB_INFO(callback_data->pMessage);
240  break;
241  case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
242  KB_WARN(callback_data->pMessage);
243  break;
244  case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
245  default:
246  KB_ERROR(callback_data->pMessage);
247  break;
248  }
249  return VK_FALSE;
250 }
Lightweight layer between platform and other engine components to enable tracing/monitoring.
b8 string_equal(const string lhs, const string rhs)
Check if the contents of two strings are equal.
Definition: string.c:118
string string_from_raw(const char *raw)
Construct a string from a raw string.
Definition: string.c:13
Custom library for interactions with strings using string views.
#define UNUSED(x)
Mark parameter as unused.
Definition: defines.h:21
#define KB_NULL
Value of an invalid ptr (nullptr).
Definition: defines.h:18
Logging system.
#define KB_ASSERT(expr,...)
Perform runtime assertion and log failures.
Definition: log.h:133
#define KB_WARN(...)
Log entry with warn log level.
Definition: log.h:161
#define KB_ERROR(...)
Log entry with error log level.
Definition: log.h:142
#define KB_TRACE(...)
Log entry with trace log level.
Definition: log.h:164
#define KB_INFO(...)
Log entry with info log level.
Definition: log.h:162