// // 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; 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_EXT_DEBUG_UTILS_EXTENSION_NAME); instance_exts.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); uint32_t availableCount = 0; vkEnumerateInstanceExtensionProperties(nullptr, &availableCount, nullptr); std::vector available(availableCount); vkEnumerateInstanceExtensionProperties(nullptr, &availableCount, available.data()); bool has_portability_extension = false; for (const VkExtensionProperties &property : available) { if (strcmp(property.extensionName, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) == 0) { has_portability_extension = true; instance_exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); } } 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; VkInstanceCreateInfo ici{ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = &dbg, .pApplicationInfo = &app, .enabledLayerCount = 1, .ppEnabledLayerNames = layers, .enabledExtensionCount = static_cast(instance_exts.size()), .ppEnabledExtensionNames = instance_exts.data(), }; if (has_portability_extension) { ici.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; } 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; } } if (int r = glfwGetPhysicalDevicePresentationSupport(instance, physicalDevice, queueFamily) == GLFW_FALSE) { return; } std::println("{}", queueFamily); std::vector devExts = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME, }; uint32_t extensionCount = 0; vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr); std::vector availableExtensions(extensionCount); vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data()); for (const auto& ext : availableExtensions) { if (strcmp(ext.extensionName, "VK_KHR_portability_subset") == 0) { devExts.push_back("VK_KHR_portability_subset"); break; } } float prio = 1.0f; VkDeviceQueueCreateInfo qci{ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = queueFamily, .queueCount = 1, .pQueuePriorities = &prio }; VkPhysicalDeviceVulkan14Features enabledVk14Features{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES, .pNext = nullptr, .globalPriorityQuery = false, .shaderSubgroupRotate = false, .shaderSubgroupRotateClustered = false, .shaderFloatControls2 = false, .shaderExpectAssume = false, .rectangularLines = false, .bresenhamLines = false, .smoothLines = false, .stippledRectangularLines = false, .stippledBresenhamLines = false, .stippledSmoothLines = false, .vertexAttributeInstanceRateDivisor = false, .vertexAttributeInstanceRateZeroDivisor = false, .indexTypeUint8 = false, .dynamicRenderingLocalRead = false, .maintenance5 = false, .maintenance6 = false, .pipelineProtectedAccess = false, .pipelineRobustness = false, .hostImageCopy = false, .pushDescriptor = false, }; VkPhysicalDeviceVulkan13Features enabledVk13Features{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, .pNext = nullptr, .robustImageAccess = false, .inlineUniformBlock = false, .descriptorBindingInlineUniformBlockUpdateAfterBind = false, .pipelineCreationCacheControl = false, .privateData = false, .shaderDemoteToHelperInvocation = false, .shaderTerminateInvocation = false, .subgroupSizeControl = false, .computeFullSubgroups = false, .synchronization2 = true, .textureCompressionASTC_HDR = false, .shaderZeroInitializeWorkgroupMemory = false, .dynamicRendering = true, .shaderIntegerDotProduct = false, .maintenance4 = false }; VkPhysicalDeviceVulkan12Features enabledVk12Features{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .pNext = &enabledVk13Features, .descriptorIndexing = true, .shaderSampledImageArrayNonUniformIndexing = true, .descriptorBindingSampledImageUpdateAfterBind = true, .descriptorBindingPartiallyBound = true, .descriptorBindingVariableDescriptorCount = true, .runtimeDescriptorArray = true, .bufferDeviceAddress = true, }; const VkPhysicalDeviceFeatures enabledVk10Features{ .samplerAnisotropy = true, }; const VkDeviceCreateInfo dci{ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &enabledVk12Features, .queueCreateInfoCount = 1, .pQueueCreateInfos = &qci, .enabledExtensionCount = (uint32_t)devExts.size(), .ppEnabledExtensionNames = devExts.data(), .pEnabledFeatures = &enabledVk10Features, }; 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); } void createSwapchain(GLFWwindow* window) { int fbWidth, fbHeight; glfwGetFramebufferSize(window, &fbWidth, &fbHeight); swapchain_extent = { static_cast(fbWidth), static_cast(fbHeight) }; VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, .pNext = nullptr, .surface = surface, }; VkSurfaceCapabilities2KHR surfCapabilities{ .sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, }; vkGetPhysicalDeviceSurfaceCapabilities2KHR(physicalDevice, &surfaceInfo, &surfCapabilities); const VkSwapchainCreateInfoKHR sci{ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = surface, .minImageCount = surfCapabilities.surfaceCapabilities.minImageCount, .imageFormat = swapchain_format.format, .imageColorSpace = swapchain_format.colorSpace, .imageExtent = swapchain_extent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = VK_PRESENT_MODE_FIFO_KHR, .clipped = VK_TRUE }; vkCreateSwapchainKHR(device, &sci, nullptr, &swapchain); uint32_t imgCount; vkGetSwapchainImagesKHR(device, swapchain, &imgCount, nullptr); std::print("imgCount: {}", imgCount); images = std::vector(imgCount); vkGetSwapchainImagesKHR(device, swapchain, &imgCount, images.data()); imageLayouts = std::vector( imgCount, VK_IMAGE_LAYOUT_UNDEFINED ); imageViews = std::vector(imgCount); const VkSemaphoreCreateInfo seci{ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, }; renderFinished.resize(imgCount); for (uint32_t i = 0; i < imgCount; i++) { VkImageViewCreateInfo ivci{}; ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; ivci.image = images[i]; ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; ivci.format = swapchain_format.format; // must match swapchain format ivci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; ivci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; ivci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; ivci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ivci.subresourceRange.baseMipLevel = 0; ivci.subresourceRange.levelCount = 1; ivci.subresourceRange.baseArrayLayer = 0; ivci.subresourceRange.layerCount = 1; vkCreateImageView(device, &ivci, nullptr, &imageViews[i]); vkCreateSemaphore(device, &seci, nullptr, &renderFinished[i]); } }