diff options
author | Edward O'Callaghan <funfunctor@folklore1984.net> | 2017-02-07 22:05:57 +1100 |
---|---|---|
committer | Edward O'Callaghan <funfunctor@folklore1984.net> | 2017-02-07 22:05:57 +1100 |
commit | 69fb536dffe9fe6aa4b78921225db9ddc228ba51 (patch) | |
tree | 22218e1985ed2c6d1065fb486ac4e4716b5a3b05 /src | |
parent | 01ac1b3060a094646d2d5fab32099a8cfffba9e4 (diff) |
src/main.cpp: Mostly implemented :/
Signed-off-by: Edward O'Callaghan <funfunctor@folklore1984.net>
Diffstat (limited to 'src')
-rw-r--r-- | src/main.cpp | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp index 43416ee..c558af8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,22 +3,451 @@ #include <iostream> #include <stdexcept> #include <functional> +#include <algorithm> +#include <vector> +#include <set> + +#include "res_mgmr.hcpp" +#include "shader_loader.hcpp" + +#define GLFW_INCLUDE_VULKAN +#include <GLFW/glfw3.h> class HelloTriangleApplication { public: void run() { + initWindow(); initVulkan(); mainLoop(); } private: + const int WIDTH = 800; + const int HEIGHT = 600; + + GLFWwindow *window; + + VDeleter<VkInstance> instance {vkDestroyInstance}; + VDeleter<VkSurfaceKHR> surface{instance, vkDestroySurfaceKHR}; + + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VDeleter<VkDevice> device{vkDestroyDevice}; + + VkQueue graphicsQueue; + VkQueue presentQueue; + + VDeleter<VkSwapchainKHR> swapChain{device, vkDestroySwapchainKHR}; + std::vector<VkImage> swapChainImages; + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; + + std::vector<VDeleter<VkImageView>> swapChainImageViews; + + struct QueueFamilyIndices { + int graphicsFamily = -1; + int presentFamily = -1; + + bool isComplete() { + return graphicsFamily >= 0 && presentFamily >= 0; + } + }; + + struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector<VkSurfaceFormatKHR> formats; + std::vector<VkPresentModeKHR> presentModes; + }; + + const std::vector<const char*> deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + + const std::vector<const char*> validationLayers = { + "VK_LAYER_LUNARG_standard_validation" + }; +//#ifdef NDEBUG + const bool enableValidationLayers = false; +//#else +// const bool enableValidationLayers = true; +//#endif + + 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(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); + createSwapChain(); + createImageViews(); + createGraphicsPipeline(); + } + + void createGraphicsPipeline() { + auto vertShaderCode = readFile("shaders/vert.spv"); + auto fragShaderCode = readFile("shaders/frag.spv"); + + VDeleter<VkShaderModule> vertShaderModule{device, vkDestroyShaderModule}; + VDeleter<VkShaderModule> fragShaderModule{device, vkDestroyShaderModule}; + createShaderModule(vertShaderCode, vertShaderModule); + createShaderModule(fragShaderCode, fragShaderModule); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; + // shaderStages ?? + } + + void createShaderModule(const std::vector<char>& code, VDeleter<VkShaderModule>& shaderModule) { + VkShaderModuleCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = (uint32_t *) code.data(); + + if (vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) + throw std::runtime_error("failed to create shader module!"); + } + + void createImageViews() { + swapChainImageViews.resize(swapChainImages.size(), VDeleter<VkImageView>{device, vkDestroyImageView}); + + for (uint32_t i = 0; i < swapChainImages.size(); i++) { + VkImageViewCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapChainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapChainImageFormat; + + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) + throw std::runtime_error("failed to create image views!"); + } + } + + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + } + + return details; + } + + void createSwapChain() { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) + imageCount = swapChainSupport.capabilities.maxImageCount; + + VkSwapchainCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + uint32_t queueFamilyIndices[] = {(uint32_t) indices.graphicsFamily, (uint32_t) indices.presentFamily}; + + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) + throw std::runtime_error("failed to create swap chain!"); + + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = extent; + } + + // utils + + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) { + if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) + return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; + + for (const auto& availableFormat : availableFormats) + if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + return availableFormat; + + return availableFormats[0]; + } + + VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> availablePresentModes) { + for (const auto& availablePresentMode : availablePresentModes) + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) + return availablePresentMode; + + return VK_PRESENT_MODE_FIFO_KHR; + } + + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { + return capabilities.currentExtent; + } else { + VkExtent2D actualExtent = {WIDTH, HEIGHT}; + + actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width )); + actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); + + return actualExtent; + } + } + + // + + void createSurface() { + if (glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) + throw std::runtime_error("failed to create window surface!"); + } + + void createLogicalDevice() { + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + + std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; + std::set<int> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily}; + + + float queuePriority = 1.0f; + for (int queueFamily : uniqueQueueFamilies) { + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceFeatures deviceFeatures = {}; + + VkDeviceCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.queueCreateInfoCount = (uint32_t) queueCreateInfos.size(); + + createInfo.pEnabledFeatures = &deviceFeatures; + + createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + + if (enableValidationLayers) { +// createInfo.enabledLayerCount = validationLayers.size(); +// createInfo.ppEnabledLayerNames = validationLayers.data(); + } else { + createInfo.enabledLayerCount = 0; + } + + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) + throw std::runtime_error("failed to create logical device!"); + + vkGetDeviceQueue(device, indices.graphicsFamily, 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue); + } + + void pickPhysicalDevice() { + uint32_t deviceCount = 0; + + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + if (deviceCount == 0) + throw std::runtime_error("failed to find GPUs with Vk support!"); + std::vector<VkPhysicalDevice> 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("failed to find sutiable GPU!"); + } + + bool isDeviceSuitable(VkPhysicalDevice device) { + QueueFamilyIndices indices = findQueueFamilies(device); + bool extensionsSupported = checkDeviceExtensionSupport(device); + return indices.isComplete() && extensionsSupported; + } + + bool checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t extensionCount; + + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + std::vector<VkExtensionProperties> availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const auto& extension : availableExtensions) + requiredExtensions.erase(extension.extensionName); + + return requiredExtensions.empty(); + } + + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for (const auto& queueFamily : queueFamilies) { + if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) + indices.graphicsFamily = i; + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + + if (queueFamily.queueCount > 0 && presentSupport) + indices.presentFamily = i; + + if (indices.isComplete()) + break; + + i++; + } + + return indices; } void mainLoop() { + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + } + + glfwDestroyWindow(window); + } + + void createInstance() { + VkApplicationInfo appInfo = {}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "Learning Vk"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); // XXX + appInfo.pEngineName = "No Engine"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); // XXX + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + + unsigned int glfwExtensionCount = 0; + const char** glfwExtensions; + + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + createInfo.enabledExtensionCount = glfwExtensionCount; + createInfo.ppEnabledExtensionNames = glfwExtensions; + +// if (enableValidationLayers && !checkValidationLayerSupprt()) +// throw std::runtime_error("validation layers requested, but not available!"); + + if (enableValidationLayers) { +// createInfo.enabledLayerCount = validationLayers.size(); +// createInfo.ppEnabledLayerNames = validationLayers.data(); + } else { + createInfo.enabledLayerCount = 0; + } + + if (vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) + throw std::runtime_error("failed to create instance!"); + } + +#if 0 + bool checkValidationLayerSupport() { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector<VkLayerProperties> 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 false; } +#endif }; int main() { |