Files
evk/evk/evkDevice.c
T
2025-07-07 10:54:09 +03:00

225 lines
9.2 KiB
C

#include "evkDevice.h"
#include "evkTypes.h"
#include "evkAllocator.h"
#define SUPPORTED_EXTENSION_FEATURE_PAIRS \
(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME , features12.descriptorIndexing ), \
(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, features12.bufferDeviceAddress), \
(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME , features13.dynamicRendering ), \
(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME , features13.synchronization2 )
#define EVK_ENABLE_EXTENSION_FEATURES(enabledExtensions,featuresStruct) \
EV_FOREACH_UDATA(EVK_ENABLE_EXTENSION_FEATURE, (enabledExtensions, featuresStruct), SUPPORTED_EXTENSION_FEATURE_PAIRS)
#define EVK_ENABLE_EXTENSION_FEATURE(ef_pair, name_flag_pair) \
{ \
evstring name = evstr(EV_HEAD name_flag_pair); \
if(ev_vec_find(&EV_HEAD ef_pair, &name) != -1) \
EV_TAIL ef_pair.EV_TAIL name_flag_pair = true; \
}
VkPhysicalDeviceDescriptorBufferFeaturesEXT descriptorBufFeature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT,
.descriptorBuffer = VK_TRUE,
// EV_DEBUG(
// .descriptorBufferCaptureReplay = VK_TRUE,
// )
};
typedef struct {
evstring name;
void* data;
void* ppNext;
} evkDeviceExtension;
const evkDeviceExtension evkSupportedExtensions[] = {
{ evstr(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME) , &descriptorBufFeature , &descriptorBufFeature.pNext },
};
const u32 evkSupportedExtensionsCount = EV_ARRSIZE(evkSupportedExtensions);
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 = {0};
device._physicalDevice = evkDetectPhysicalDevice(createInfo.instance, createInfo.physicalDeviceType);
device._instance = createInfo.instance;
VkPhysicalDeviceProperties2 physicalDeviceProperties = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
};
void** pNextProps = (void**)&physicalDeviceProperties.pNext;
evstring VkDescriptorBufferExtName = evstr(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME);
if(ev_vec_find(&createInfo.deviceExtensions, &VkDescriptorBufferExtName) != -1)
{
device.props.desc_buf = (VkPhysicalDeviceDescriptorBufferPropertiesEXT){ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT };
*pNextProps = &device.props.desc_buf;
pNextProps = &device.props.desc_buf.pNext;
}
vkGetPhysicalDeviceProperties2(device._physicalDevice, &physicalDeviceProperties);
device.props.limits = physicalDeviceProperties.properties.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 = (void**)&vkDeviceCreateInfo.pNext;
for(int i = 0; i < evkSupportedExtensionsCount; i++)
{
if(ev_vec_find(&createInfo.deviceExtensions, &evkSupportedExtensions[i].name) != -1)
{
*pNext = evkSupportedExtensions[i].data;
pNext = evkSupportedExtensions[i].ppNext;
}
}
EVK_ENABLE_EXTENSION_FEATURES(createInfo.deviceExtensions, createInfo.enabledFeatures);
VkPhysicalDeviceFeatures2 enabledFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.features = createInfo.enabledFeatures.features10,
.pNext = &createInfo.enabledFeatures.features11,
};
createInfo.enabledFeatures.features11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
createInfo.enabledFeatures.features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
createInfo.enabledFeatures.features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
createInfo.enabledFeatures.features11.pNext = &createInfo.enabledFeatures.features12;
createInfo.enabledFeatures.features12.pNext = &createInfo.enabledFeatures.features13;
*pNext = &enabledFeatures;
pNext = NULL;
vkDeviceCreateInfo.enabledExtensionCount = vec_len(&createInfo.deviceExtensions);
vkDeviceCreateInfo.ppEnabledExtensionNames = (const char* const*) 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];
}
}
}
EVK_ASSERT(vkCreateDevice(device._physicalDevice, &vkDeviceCreateInfo, evkGetAllocationCallbacks(), &device.vk));
vec_fini(&queueCreateInfoList);
vec_fini(&priorities);
return device;
}
void evkDestroyDevice(evkDevice device)
{
vkDestroyDevice(device.vk, evkGetAllocationCallbacks());
}