// // Created by Vicente Ferrari Smith on 12.02.26. // #include "init.h" #include #include VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) { const char* severityStr = "UNKNOWN"; if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) severityStr = "VERBOSE"; else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) severityStr = "INFO"; else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) severityStr = "WARNING"; else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) severityStr = "ERROR"; const char* typeStr = ""; if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) typeStr = "GENERAL"; else if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) typeStr = "VALIDATION"; else if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) typeStr = "PERFORMANCE"; std::fprintf(stderr, "\n[VULKAN %s][%s]\n%s\n", severityStr, typeStr, callbackData->pMessage); // Print associated objects (very useful on MoltenVK) for (uint32_t i = 0; i < callbackData->objectCount; i++) { const VkDebugUtilsObjectNameInfoEXT& obj = callbackData->pObjects[i]; std::fprintf(stderr, " Object %u: type=%d handle=0x%llx name=%s\n", i, obj.objectType, static_cast(obj.objectHandle), obj.pObjectName ? obj.pObjectName : "null"); } return VK_FALSE; // Do not abort Vulkan call } int createInstance(GLFWwindow *window) { volkInitialize(); uint32_t extensions_count; const char** extensions = glfwGetRequiredInstanceExtensions(&extensions_count); std::vector instance_exts; instance_exts.reserve(extensions_count + 1); // Copy GLFW-required extensions for (uint32_t i = 0; i < extensions_count; i++) { instance_exts.push_back(extensions[i]); } // Append portability enumeration (MANDATORY on macOS) instance_exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); instance_exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); for (auto& ext : instance_exts) { std::print("extension {}\n", ext); } constexpr VkApplicationInfo app{ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_4, }; const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; VkDebugUtilsMessengerCreateInfoEXT dbg{}; dbg.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; dbg.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; dbg.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; dbg.pfnUserCallback = debugCallback; const VkInstanceCreateInfo ici{ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = &dbg, .flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR, .pApplicationInfo = &app, .enabledLayerCount = 1, .ppEnabledLayerNames = layers, .enabledExtensionCount = static_cast(instance_exts.size()), .ppEnabledExtensionNames = instance_exts.data(), }; VkResult res = vkCreateInstance(&ici, nullptr, &instance); if (res != VK_SUCCESS) { printf("vkCreateInstance failed: %d\n", res); return -1; } volkLoadInstance(instance); VkDebugUtilsMessengerEXT messenger; vkCreateDebugUtilsMessengerEXT(instance, &dbg, nullptr, &messenger); uint32_t layerCount = 0; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); return 0; } void createSurface(GLFWwindow *window) { glfwCreateWindowSurface(instance, window, nullptr, &surface); } void createDevice() { uint32_t deviceCount = 0; uint32_t res = vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); if (res != VK_SUCCESS || deviceCount == 0) { printf("vkEnumeratePhysicalDevices failed or found no devices (%d)\n", res); } std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); printf("Found %u physical device(s)\n", deviceCount); for (uint32_t i = 0; i < deviceCount; i++) { VkPhysicalDeviceProperties props{}; vkGetPhysicalDeviceProperties(devices[i], &props); printf("Device %u:\n", i); printf(" Name: %s\n", props.deviceName); printf(" Type: %u\n", props.deviceType); printf(" API version: %u.%u.%u\n", VK_VERSION_MAJOR(props.apiVersion), VK_VERSION_MINOR(props.apiVersion), VK_VERSION_PATCH(props.apiVersion)); } physicalDevice = devices[0]; queueFamily = 0; uint32_t qCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &qCount, nullptr); std::vector qprops(qCount); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &qCount, qprops.data()); for (uint32_t i = 0; i < qCount; i++) { VkBool32 present = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &present); if ((qprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && present) { queueFamily = i; break; } } std::println("{}", queueFamily); const char* devExts[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME, VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, "VK_KHR_portability_subset" }; float prio = 1.0f; VkDeviceQueueCreateInfo qci{ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = queueFamily, .queueCount = 1, .pQueuePriorities = &prio }; VkPhysicalDeviceDescriptorIndexingFeatures indexingFeatures{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, .pNext = nullptr, .shaderSampledImageArrayNonUniformIndexing = VK_TRUE, .descriptorBindingSampledImageUpdateAfterBind = VK_TRUE, .descriptorBindingPartiallyBound = VK_TRUE, .runtimeDescriptorArray = VK_TRUE }; VkPhysicalDeviceTimelineSemaphoreFeatures timeline{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, .pNext = &indexingFeatures, .timelineSemaphore = VK_TRUE }; VkPhysicalDeviceSynchronization2Features sync2{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, .pNext = &timeline, .synchronization2 = VK_TRUE }; VkPhysicalDeviceDynamicRenderingFeatures dyn{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, .pNext = &sync2, .dynamicRendering = VK_TRUE }; const VkDeviceCreateInfo dci{ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &dyn, .queueCreateInfoCount = 1, .pQueueCreateInfos = &qci, .enabledExtensionCount = 7, .ppEnabledExtensionNames = devExts }; vkCreateDevice(physicalDevice, &dci, nullptr, &device); volkLoadDevice(device); vkGetDeviceQueue(device, queueFamily, 0, &graphics_queue); VmaAllocatorCreateInfo allocatorCreateInfo = { .flags = 0, .physicalDevice = physicalDevice, .device = device, .preferredLargeHeapBlockSize = 0, .pAllocationCallbacks = nullptr, .pDeviceMemoryCallbacks = nullptr, .pHeapSizeLimit = nullptr, .pVulkanFunctions = nullptr, .instance = instance, .vulkanApiVersion = VK_API_VERSION_1_4, .pTypeExternalMemoryHandleTypes = nullptr, }; VmaVulkanFunctions vulkan_functions = {}; res = vmaImportVulkanFunctionsFromVolk(&allocatorCreateInfo, &vulkan_functions); allocatorCreateInfo.pVulkanFunctions = &vulkan_functions; res = vmaCreateAllocator(&allocatorCreateInfo, &allocator); }