#include #include #define GLFW_INCLUDE_VULKAN #include #include #include #include #include #include const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" }; #ifdef NDEBUG const bool enableValidationLayers = false; #else const bool enableValidationLayers = true; #endif class HelloTriangleApplication { public: void run() { initWindow(); initVulkan(); mainLoop(); cleanup(); } private: GLFWwindow* window; VkInstance instance; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkDevice device; VkQueue graphicsQueue; const uint32_t WIDTH = 800; const uint32_t HEIGHT = 600; void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); } void initVulkan() { createInstance(); pickPhysicalDevice(); createLogicalDevice(); } void createLogicalDevice() { QueueFamilyIndices indices = findQueueFamilies(physicalDevice); VkDeviceQueueCreateInfo queueCreateInfo{}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value(); queueCreateInfo.queueCount = 1; float queuePriority = 1.0f; queueCreateInfo.pQueuePriorities = &queuePriority; VkPhysicalDeviceFeatures deviceFeatures{}; VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.pQueueCreateInfos = &queueCreateInfo; createInfo.queueCreateInfoCount = 1; createInfo.pEnabledFeatures = &deviceFeatures; /*createInfo.enabledExtensionCount = 1; std::vector extensions(1); extensions.emplace_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); createInfo.ppEnabledExtensionNames = extensions.data();*/ if (enableValidationLayers) { createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } VkResult result = vkCreateDevice(physicalDevice, &createInfo, nullptr, &device); if (result != VK_SUCCESS) { std::cout << "error: " << result << std::endl; throw std::runtime_error("failed to create logical device"); } vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); } void pickPhysicalDevice() { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); if (deviceCount == 0) { throw std::runtime_error("failed to find GPUs with Vulkan support!"); } std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); for (const auto& device : devices) { if (isDeviceSuitable(device)) { physicalDevice = device; break; } } if (physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("could not find a suitable GPU"); } } bool isDeviceSuitable(VkPhysicalDevice device) { /*VkPhysicalDeviceProperties props; vkGetPhysicalDeviceProperties(device, &props); VkPhysicalDeviceFeatures features; vkGetPhysicalDeviceFeatures(device, &features); return props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && features.geometryShader;*/ QueueFamilyIndices indices = findQueueFamilies(device); return indices.isComplete(); } struct QueueFamilyIndices { std::optional graphicsFamily; bool isComplete() { return graphicsFamily.has_value(); } }; QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); std::vector queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); int i = 0; for (const auto& queueFamily : queueFamilies) { if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily = i; } if (indices.isComplete()) { break; } i++; } return indices; } bool checkValidationLayerSupport() { uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); std::vector availableLayers(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); for (const char* layerName : validationLayers) { bool layerFound = false; for (const auto& layerProperties : availableLayers) { if (strcmp(layerName, layerProperties.layerName) == 0) { layerFound = true; break; } } if (!layerFound) { return false; } } return true; } void createInstance() { VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Hello Triangle"; appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.pEngineName = "No Engine"; appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.apiVersion = VK_API_VERSION_1_0; VkInstanceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &appInfo; std::vector requiredExtensions; uint32_t glfwExtensionCount = 0; const char** glfwExtensions; // Enable glfw extensions glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); for (uint32_t i = 0; i < glfwExtensionCount; i++) { requiredExtensions.emplace_back(glfwExtensions[i]); } // Enable Portability Enumeration extension (required for macOS) requiredExtensions.emplace_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; createInfo.enabledExtensionCount = (uint32_t) requiredExtensions.size(); createInfo.ppEnabledExtensionNames = requiredExtensions.data(); // Check validation layers if (enableValidationLayers) { if (!checkValidationLayerSupport()) { throw std::runtime_error("validation layers requested, but not available!"); } createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; } std::cout << "Validation layers: OK" << std::endl; VkResult result = vkCreateInstance(&createInfo, nullptr, &instance); if (result != VK_SUCCESS) { std::cout << "error creating instance: " << result << std::endl; throw std::runtime_error("failed to create instance!"); } std::cout << "Created instance" << std::endl; // Check for available extensions uint32_t extensionCount = 0; vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); std::vector extensions(extensionCount); vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); std::cout << "available extensions:" << std::endl; for (const auto& extension : extensions) { std::cout << '\t' << extension.extensionName; for (const char* required : requiredExtensions) { if (strcmp(required, extension.extensionName) == 0) { std::cout << " *"; } } std::cout << std::endl; } } void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } } void cleanup() { vkDestroyDevice(device, nullptr); vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); glfwTerminate(); } }; int main() { HelloTriangleApplication app; try { app.run(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }