diff --git a/Makefile b/Makefile index 0dc246a..dffd824 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ VK_LAYER_PATH=${VULKAN_SDK}/macOS/share/vulkan/explicit_layer.d .PHONY: main main: main.cpp # compile - clang++ -I${_BREW_PREFIX}/include -I${VULKAN_SDK}/macOS/include -c main.cpp -o main.o + clang++ -I${_BREW_PREFIX}/include -I${VULKAN_SDK}/macOS/include --std=c++17 -c main.cpp -o main.o # link clang++ -L${_BREW_PREFIX}/lib -lglfw.3.3 -L${VULKAN_SDK}/macOS/lib -lvulkan.1.3.236 -l vulkan.1 main.o -o main diff --git a/main.cpp b/main.cpp index bcc5a70..0ac51a0 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include +#include #define GLFW_INCLUDE_VULKAN #include @@ -6,6 +7,17 @@ #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: @@ -20,6 +32,11 @@ private: GLFWwindow* window; VkInstance instance; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + + VkDevice device; + + VkQueue graphicsQueue; const uint32_t WIDTH = 800; const uint32_t HEIGHT = 600; @@ -32,6 +49,149 @@ private: 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; @@ -64,7 +224,19 @@ private: createInfo.enabledExtensionCount = (uint32_t) requiredExtensions.size(); createInfo.ppEnabledExtensionNames = requiredExtensions.data(); - createInfo.enabledLayerCount = 0; + + // 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) { @@ -94,10 +266,6 @@ private: } } - void initVulkan() { - createInstance(); - } - void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); @@ -105,6 +273,7 @@ private: } void cleanup() { + vkDestroyDevice(device, nullptr); vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window);