Files
evk/evk/evkDevice.c
2025-07-02 12:37:06 +03:00

177 lines
6.9 KiB
C

#include "evkDevice.h"
#include "evkTypes.h"
#include "evkAllocator.h"
evstring VkDynamicRenderingExtName = evstr("VK_KHR_dynamic_rendering");
evstring VkSync2ExtName = evstr("VK_KHR_synchronization2");
VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeature = (VkPhysicalDeviceDynamicRenderingFeaturesKHR) {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR,
.dynamicRendering = VK_TRUE,
};
VkPhysicalDeviceSynchronization2Features sync2Features = (VkPhysicalDeviceSynchronization2Features) {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES,
.synchronization2 = VK_TRUE,
};
VkPhysicalDevice evkDetectPhysicalDevice(evkInstance instance, VkPhysicalDeviceType deviceType)
{
VkPhysicalDevice chosenDevice = EV_INVALID(VkPhysicalDevice);
u32 deviceCount = 0;
vec(VkPhysicalDevice) physicalDevices = vec_init(VkPhysicalDevice);
vkEnumeratePhysicalDevices(instance.vk, &deviceCount, NULL);
vec_setlen(&physicalDevices, deviceCount);
vkEnumeratePhysicalDevices(instance.vk, &deviceCount, physicalDevices);
// TODO Add a more robust/flexible way to pick a GPU
for(u32 i = 0; i < deviceCount; i++)
{
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(physicalDevices[i], &deviceProperties);
if(deviceProperties.deviceType == deviceType)
{
chosenDevice = physicalDevices[i];
break;
}
}
vec_fini(&physicalDevices);
return chosenDevice;
}
evkDevice evkCreateDevice(evkDeviceCreateInfo createInfo)
{
evkDevice device;
device._physicalDevice = evkDetectPhysicalDevice(createInfo.instance, createInfo.physicalDeviceType);
device._instance = createInfo.instance;
VkPhysicalDeviceProperties physicalDeviceProperties;
vkGetPhysicalDeviceProperties(device._physicalDevice, &physicalDeviceProperties);
device.limits = physicalDeviceProperties.limits;
for (u32 i = 0; i < MAX_QUEUE_FAMILIES; i++) {
device.queueFamilies[i] = (evkDeviceQueueFamily){
.familyIndex = -1,
.allocatedQueueCount = 0
};
}
VkDeviceCreateInfo vkDeviceCreateInfo = __EV_VEC_EMPTY_ARRAY;
vkDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
void** pNext = &vkDeviceCreateInfo.pNext;
if(ev_vec_find(&createInfo.deviceExtensions, &VkDynamicRenderingExtName) != -1)
{
*pNext = &dynamicRenderingFeature;
pNext = &dynamicRenderingFeature.pNext;
}
if(ev_vec_find(&createInfo.deviceExtensions, &VkSync2ExtName) != -1)
{
*pNext = &sync2Features;
pNext = &sync2Features.pNext;
}
vkDeviceCreateInfo.enabledExtensionCount = vec_len(&createInfo.deviceExtensions);
vkDeviceCreateInfo.ppEnabledExtensionNames = createInfo.deviceExtensions;
// Getting the total number of queues requested from the device
u32 totalQueueCount = 0;
for (u32 i = 0; i < vec_len(&createInfo.queueRequirements); i++) {
totalQueueCount += createInfo.queueRequirements[i].count;
}
const f32 DEFAULT_PRIORITY = 1.0f;
// A priorities list that can be passed with any VkQueueCreteInfo (doable since we have no custom priorities)
vec(f32) priorities = vec_init(f32);
vec_setlen(&priorities, totalQueueCount);
for (u32 i = 0; i < totalQueueCount; i++) {
priorities[i] = DEFAULT_PRIORITY;
}
vec(VkDeviceQueueCreateInfo) queueCreateInfoList = vec_init(VkDeviceQueueCreateInfo);
if (createInfo.queueRequirements) {
// Retrieving the queue family properties into a vector
u32 queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device._physicalDevice, &queueFamilyCount, NULL);
vec(VkQueueFamilyProperties) queueFamilyProperties = vec_init(VkQueueFamilyProperties);
vec_setlen(&queueFamilyProperties, queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device._physicalDevice, &queueFamilyCount, queueFamilyProperties);
// Creating VkDeviceQueueCreateInfos from requirements and family properties
// NOTICE: Currently, there can be no queues of a specific type in 2 different
// families. While that can be considered a limitation, the application targeted
// by this abstraction aren't expected to be complex enough to need it.
for (u32 familyIndex = 0; familyIndex < queueFamilyCount; familyIndex++) {
VkQueueFamilyProperties currentFamilyProperties = queueFamilyProperties[familyIndex];
u32 availableQueuesInFamily = currentFamilyProperties.queueCount;
for (u32 reqIndex = 0; reqIndex < vec_len(&createInfo.queueRequirements); reqIndex++) {
evkDeviceQueueRequirement currentRequirement = createInfo.queueRequirements[reqIndex];
// Testing compatibility of queue family and requirement
if (currentRequirement.flags & currentFamilyProperties.queueFlags) {
u32 requiredCount = currentRequirement.count;
while (availableQueuesInFamily > 0 && requiredCount > 0) {
// If the queue family is not already in the vector, push it.
if (vec_len(&queueCreateInfoList) <= familyIndex) {
VkDeviceQueueCreateInfo newQueueCreateInfo = { 0 };
newQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
newQueueCreateInfo.queueFamilyIndex = familyIndex;
newQueueCreateInfo.pQueuePriorities = priorities;
vec_push(&queueCreateInfoList, &newQueueCreateInfo);
}
u32 allocationCount = (availableQueuesInFamily>requiredCount)?requiredCount:availableQueuesInFamily;
queueCreateInfoList[familyIndex].queueCount += allocationCount;
device.queueFamilies[currentRequirement.flags].familyIndex = familyIndex;
device.queueFamilies[currentRequirement.flags].allocatedQueueCount += allocationCount;
// Allowing retrieval of families according to their actual flags
device.queueFamilies[currentFamilyProperties.queueFlags].familyIndex = familyIndex;
device.queueFamilies[currentFamilyProperties.queueFlags].allocatedQueueCount += allocationCount;
// Updating the loop condition variables
availableQueuesInFamily -= allocationCount;
requiredCount -= allocationCount;
}
// Updating the requirement's count after allocation
createInfo.queueRequirements[reqIndex].count = requiredCount;
}
}
}
vkDeviceCreateInfo.pQueueCreateInfos = queueCreateInfoList;
vkDeviceCreateInfo.queueCreateInfoCount = (u32)vec_len(&queueCreateInfoList);
vec_fini(&queueFamilyProperties);
}
// Allowing the retrieval of non-requested, but allocated, queues is possible.
// Example: Allocating COMPUTE | GRAPHICS then requesting GRAPHICS should
// return the already allocated queue
for (i32 i = MAX_QUEUE_FAMILIES - 1; i >= 0; i--) {
for (i32 j = i - 1; j >= 0; j--) {
if (device.queueFamilies[j].familyIndex == -1 && (i & j) == j) {
device.queueFamilies[j] = device.queueFamilies[i];
}
}
}
vkCreateDevice(device._physicalDevice, &vkDeviceCreateInfo, evkGetAllocationCallbacks(), &device.vk);
vec_fini(&queueCreateInfoList);
vec_fini(&priorities);
return device;
}
void evkDestroyDevice(evkDevice device)
{
vkDestroyDevice(device.vk, evkGetAllocationCallbacks());
}