1 #include <kiba/renderer/vulkan/device.h>
3 #include <kiba/containers/array.h>
9 b8 vulkan_physical_device_meets_requirements(VkPhysicalDevice device,
10 VkPhysicalDeviceFeatures *features,
11 VkPhysicalDeviceProperties *props,
14 b8 vulkan_queue_create(VkDevice logical_device,
vulkan_queue *queue,
const VkAllocationCallbacks *alloc);
15 void vulkan_queue_destroy(VkDevice logical_device,
vulkan_queue *queue,
const VkAllocationCallbacks *alloc);
18 if (!vulkan_select_physical_device(context)) {
19 KB_ERROR(
"failed to find GPU meeting the requirements");
22 if (!vulkan_select_logical_device(context)) {
23 KB_ERROR(
"failed to create logical device");
31 vulkan_queue_destroy(context->device.logical, &context->device.graphics_queue, &context->alloc.vulkan_callbacks);
32 vulkan_queue_destroy(context->device.logical, &context->device.transfer_queue, &context->alloc.vulkan_callbacks);
33 vulkan_queue_destroy(context->device.logical, &context->device.present_queue, &context->alloc.vulkan_callbacks);
34 vulkan_queue_destroy(context->device.logical, &context->device.compute_queue, &context->alloc.vulkan_callbacks);
35 vkDestroyDevice(context->device.logical, &context->alloc.vulkan_callbacks);
39 u32 physical_device_count = 0;
40 VK_CALL_B8(vkEnumeratePhysicalDevices(context->instance.instance, &physical_device_count, 0));
41 if (physical_device_count == 0) {
42 KB_INFO(
"found no device that supports vulkan");
46 array_of(VkPhysicalDevice) physical_devices =
47 array_create(VkPhysicalDevice, physical_device_count, &context->alloc.kiba_alloc);
48 if (physical_devices ==
KB_NULL) {
49 KB_ERROR(
"unable to allocate enough memory for list of physical devices");
52 VK_CALL_B8(vkEnumeratePhysicalDevices(context->instance.instance, &physical_device_count, physical_devices));
53 array_resize(&physical_devices, physical_device_count);
56 array_for_each(
const VkPhysicalDevice, device, physical_devices) {
57 VkPhysicalDeviceProperties physical_device_properties;
58 VkPhysicalDeviceFeatures physical_device_features;
59 vkGetPhysicalDeviceProperties(*device, &physical_device_properties);
60 vkGetPhysicalDeviceFeatures(*device, &physical_device_features);
61 KB_INFO(
"checking device {raw_string} against requirements", physical_device_properties.deviceName);
62 if (vulkan_physical_device_meets_requirements(*device,
63 &physical_device_features,
64 &physical_device_properties,
66 KB_INFO(
"device {raw_string} meets requirements", physical_device_properties.deviceName);
67 context->device.physical = *device;
71 KB_INFO(
"skipping physical device {raw_string} because it does not match the requirements",
72 physical_device_properties.deviceName);
74 array_destroy(&physical_devices);
79 const usize max_different_queue = 8;
81 u32 indices[max_different_queue];
83 indices[index++] = context->device.graphics_queue.index;
84 if (context->device.graphics_queue.index != context->device.present_queue.index) {
85 indices[index++] = context->device.present_queue.index;
87 if (context->device.graphics_queue.index != context->device.transfer_queue.index) {
88 indices[index++] = context->device.transfer_queue.index;
92 VkDeviceQueueCreateInfo queue_create_infos[max_different_queue];
93 for (u32 i = 0; i < index; ++i) {
94 queue_create_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
95 queue_create_infos[i].queueFamilyIndex = indices[i];
97 queue_create_infos[i].queueCount = 1;
98 queue_create_infos[i].flags = 0;
99 queue_create_infos[i].pNext = 0;
100 queue_create_infos[i].pQueuePriorities = &high_prio;
103 VkDeviceCreateInfo device_create_info = {
104 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
105 .pQueueCreateInfos = queue_create_infos,
106 .queueCreateInfoCount = index,
107 .pEnabledFeatures = &context->device.requirements.features,
108 .ppEnabledExtensionNames = context->device.requirements.extensions,
109 .enabledExtensionCount = (u32) array_size(context->device.requirements.extensions),
111 VK_CALL_B8(vkCreateDevice(context->device.physical,
113 &context->alloc.vulkan_callbacks,
114 &context->device.logical));
116 if (!vulkan_queue_create(context->device.logical,
117 &context->device.graphics_queue,
118 &context->alloc.vulkan_callbacks)) {
119 KB_ERROR(
"failed to initialize graphics queue");
120 context->device.graphics_queue.available =
false;
123 if (!vulkan_queue_create(context->device.logical,
124 &context->device.transfer_queue,
125 &context->alloc.vulkan_callbacks)) {
126 KB_ERROR(
"failed to initialize transfer queue");
127 context->device.transfer_queue.available =
false;
130 if (!vulkan_queue_create(context->device.logical,
131 &context->device.present_queue,
132 &context->alloc.vulkan_callbacks)) {
133 KB_ERROR(
"failed to initialize present queue");
134 context->device.present_queue.available =
false;
137 if (!vulkan_queue_create(context->device.logical,
138 &context->device.compute_queue,
139 &context->alloc.vulkan_callbacks)) {
140 KB_ERROR(
"failed to initialize compute queue");
141 context->device.compute_queue.available =
false;
147 b8 vulkan_physical_device_meets_requirements(VkPhysicalDevice device,
148 VkPhysicalDeviceFeatures *features,
149 VkPhysicalDeviceProperties *props,
153 if (context->device.requirements.discrete_gpu && props->deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
154 KB_INFO(
"device is not viable as its not a discrete GPU");
164 u32 queue_family_count = 0;
165 vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, 0);
167 array_of(VkQueueFamilyProperties) queue_props =
168 array_create(VkQueueFamilyProperties, queue_family_count, &context->alloc.kiba_alloc);
170 KB_INFO(
"unable to allocate enough memory for list of queue properties");
173 vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_props);
174 array_resize(&queue_props, queue_family_count);
176 for (u32 i = 0; i < queue_family_count; ++i) {
177 if (queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT && !context->device.graphics_queue.available) {
178 graphics_queue.available =
true;
179 graphics_queue.index = i;
180 VkBool32 present_support =
false;
181 VK_CALL_B8(vkGetPhysicalDeviceSurfaceSupportKHR(device, i, context->surface, &present_support));
182 if (present_support) {
183 present_queue.available =
true;
184 present_queue.index = i;
187 if (queue_props[i].queueFlags & VK_QUEUE_TRANSFER_BIT) {
188 transfer_queue.available =
true;
189 transfer_queue.index = i;
191 if (queue_props[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
192 compute_queue.available =
true;
193 compute_queue.index = i;
196 array_destroy(&queue_props);
197 if (context->device.requirements.graphics && !graphics_queue.available) {
198 KB_INFO(
"device does not support required graphics queue");
201 if (context->device.requirements.transfer && !transfer_queue.available) {
202 KB_INFO(
"device does not support required transfer queue");
205 if (context->device.requirements.present && !present_queue.available) {
206 KB_INFO(
"device does not support required present queue");
209 if (context->device.requirements.compute && !compute_queue.available) {
210 KB_INFO(
"device does not support required compute queue");
215 u32 extension_count = 0;
216 vkEnumerateDeviceExtensionProperties(device, 0, &extension_count, 0);
218 array_of(VkExtensionProperties) extensions =
219 array_create(VkExtensionProperties, extension_count, &context->alloc.kiba_alloc);
221 KB_INFO(
"unable to allocate enough memory for list of extensions");
224 vkEnumerateDeviceExtensionProperties(device, 0, &extension_count, extensions);
225 array_resize(&extensions, extension_count);
227 u32 required_extension_count = (u32) array_size(context->device.requirements.extensions);
228 b8 has_extensions =
true;
229 if (extension_count >= required_extension_count) {
230 array_for_each(
const char *, extension, context->device.requirements.extensions) {
232 for (u32 j = 0; j < extension_count; ++j) {
239 KB_INFO(
"required extension {raw_string} not supported by device", *extension);
240 has_extensions =
false;
244 KB_INFO(
"device cannot support all required extensions");
245 has_extensions =
false;
248 array_destroy(&extensions);
250 context->device.graphics_queue = graphics_queue;
251 context->device.transfer_queue = transfer_queue;
252 context->device.present_queue = present_queue;
253 context->device.compute_queue = compute_queue;
255 return has_extensions;
258 b8 vulkan_queue_create(VkDevice logical_device,
vulkan_queue *queue,
const VkAllocationCallbacks *alloc) {
259 if (!queue->available)
261 vkGetDeviceQueue(logical_device, queue->index, 0, &queue->queue);
263 VkCommandPoolCreateInfo pool_create_info = {
264 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
265 .queueFamilyIndex = queue->index,
266 .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
268 VK_CALL_B8(vkCreateCommandPool(logical_device, &pool_create_info, alloc, &queue->command_pool));
270 VkCommandBufferAllocateInfo cb_allocate_info = {
271 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
272 .commandPool = queue->command_pool,
273 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
274 .commandBufferCount = 1,
276 VK_CALL_B8(vkAllocateCommandBuffers(logical_device, &cb_allocate_info, &queue->command_buffer));
280 void vulkan_queue_destroy(VkDevice logical_device,
vulkan_queue *queue,
const VkAllocationCallbacks *alloc) {
281 if (queue->available) {
282 vkDestroyCommandPool(logical_device, queue->command_pool, alloc);
b8 string_equal(const string lhs, const string rhs)
Check if the contents of two strings are equal.
string string_from_raw(const char *raw)
Construct a string from a raw string.
Custom library for interactions with strings using string views.
#define UNUSED(x)
Mark parameter as unused.
#define KB_NULL
Value of an invalid ptr (nullptr).
#define KB_ERROR(...)
Log entry with error log level.
#define KB_INFO(...)
Log entry with info log level.