Permalink
Cannot retrieve contributors at this time
1105 lines (855 sloc)
42.1 KB
| #define GLFW_INCLUDE_VULKAN | |
| #include <GLFW/glfw3.h> | |
| #include <glm/glm.hpp> | |
| #include <iostream> | |
| #include <fstream> | |
| #include <stdexcept> | |
| #include <algorithm> | |
| #include <vector> | |
| #include <cstring> | |
| #include <cstdlib> | |
| #include <cstdint> | |
| #include <array> | |
| #include <optional> | |
| #include <set> | |
| const uint32_t WIDTH = 800; | |
| const uint32_t HEIGHT = 600; | |
| const int MAX_FRAMES_IN_FLIGHT = 2; | |
| const std::vector<const char*> validationLayers = { | |
| "VK_LAYER_KHRONOS_validation" | |
| }; | |
| const std::vector<const char*> deviceExtensions = { | |
| VK_KHR_SWAPCHAIN_EXTENSION_NAME | |
| }; | |
| #ifdef NDEBUG | |
| const bool enableValidationLayers = false; | |
| #else | |
| const bool enableValidationLayers = true; | |
| #endif | |
| VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { | |
| auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); | |
| if (func != nullptr) { | |
| return func(instance, pCreateInfo, pAllocator, pDebugMessenger); | |
| } else { | |
| return VK_ERROR_EXTENSION_NOT_PRESENT; | |
| } | |
| } | |
| void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { | |
| auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); | |
| if (func != nullptr) { | |
| func(instance, debugMessenger, pAllocator); | |
| } | |
| } | |
| struct QueueFamilyIndices { | |
| std::optional<uint32_t> graphicsFamily; | |
| std::optional<uint32_t> presentFamily; | |
| bool isComplete() { | |
| return graphicsFamily.has_value() && presentFamily.has_value(); | |
| } | |
| }; | |
| struct SwapChainSupportDetails { | |
| VkSurfaceCapabilitiesKHR capabilities; | |
| std::vector<VkSurfaceFormatKHR> formats; | |
| std::vector<VkPresentModeKHR> presentModes; | |
| }; | |
| struct Vertex { | |
| glm::vec2 pos; | |
| glm::vec3 color; | |
| static VkVertexInputBindingDescription getBindingDescription() { | |
| VkVertexInputBindingDescription bindingDescription{}; | |
| bindingDescription.binding = 0; | |
| bindingDescription.stride = sizeof(Vertex); | |
| bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; | |
| return bindingDescription; | |
| } | |
| static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions() { | |
| std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions{}; | |
| attributeDescriptions[0].binding = 0; | |
| attributeDescriptions[0].location = 0; | |
| attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; | |
| attributeDescriptions[0].offset = offsetof(Vertex, pos); | |
| attributeDescriptions[1].binding = 0; | |
| attributeDescriptions[1].location = 1; | |
| attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; | |
| attributeDescriptions[1].offset = offsetof(Vertex, color); | |
| return attributeDescriptions; | |
| } | |
| }; | |
| const std::vector<Vertex> vertices = { | |
| {{0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}}, | |
| {{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}}, | |
| {{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}} | |
| }; | |
| class HelloTriangleApplication { | |
| public: | |
| void run() { | |
| initWindow(); | |
| initVulkan(); | |
| mainLoop(); | |
| cleanup(); | |
| } | |
| private: | |
| GLFWwindow* window; | |
| VkInstance instance; | |
| VkDebugUtilsMessengerEXT debugMessenger; | |
| VkSurfaceKHR surface; | |
| VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; | |
| VkDevice device; | |
| VkQueue graphicsQueue; | |
| VkQueue presentQueue; | |
| VkSwapchainKHR swapChain; | |
| std::vector<VkImage> swapChainImages; | |
| VkFormat swapChainImageFormat; | |
| VkExtent2D swapChainExtent; | |
| std::vector<VkImageView> swapChainImageViews; | |
| std::vector<VkFramebuffer> swapChainFramebuffers; | |
| VkRenderPass renderPass; | |
| VkPipelineLayout pipelineLayout; | |
| VkPipeline graphicsPipeline; | |
| VkCommandPool commandPool; | |
| VkBuffer vertexBuffer; | |
| VkDeviceMemory vertexBufferMemory; | |
| std::vector<VkCommandBuffer> commandBuffers; | |
| std::vector<VkSemaphore> imageAvailableSemaphores; | |
| std::vector<VkSemaphore> renderFinishedSemaphores; | |
| std::vector<VkFence> inFlightFences; | |
| std::vector<VkFence> imagesInFlight; | |
| size_t currentFrame = 0; | |
| bool framebufferResized = false; | |
| void initWindow() { | |
| glfwInit(); | |
| glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
| window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); | |
| glfwSetWindowUserPointer(window, this); | |
| glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); | |
| } | |
| static void framebufferResizeCallback(GLFWwindow* window, int width, int height) { | |
| auto app = reinterpret_cast<HelloTriangleApplication*>(glfwGetWindowUserPointer(window)); | |
| app->framebufferResized = true; | |
| } | |
| void initVulkan() { | |
| createInstance(); | |
| setupDebugMessenger(); | |
| createSurface(); | |
| pickPhysicalDevice(); | |
| createLogicalDevice(); | |
| createSwapChain(); | |
| createImageViews(); | |
| createRenderPass(); | |
| createGraphicsPipeline(); | |
| createFramebuffers(); | |
| createCommandPool(); | |
| createVertexBuffer(); | |
| createCommandBuffers(); | |
| createSyncObjects(); | |
| } | |
| void mainLoop() { | |
| while (!glfwWindowShouldClose(window)) { | |
| glfwPollEvents(); | |
| drawFrame(); | |
| } | |
| vkDeviceWaitIdle(device); | |
| } | |
| void cleanupSwapChain() { | |
| for (auto framebuffer : swapChainFramebuffers) { | |
| vkDestroyFramebuffer(device, framebuffer, nullptr); | |
| } | |
| vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data()); | |
| vkDestroyPipeline(device, graphicsPipeline, nullptr); | |
| vkDestroyPipelineLayout(device, pipelineLayout, nullptr); | |
| vkDestroyRenderPass(device, renderPass, nullptr); | |
| for (auto imageView : swapChainImageViews) { | |
| vkDestroyImageView(device, imageView, nullptr); | |
| } | |
| vkDestroySwapchainKHR(device, swapChain, nullptr); | |
| } | |
| void cleanup() { | |
| cleanupSwapChain(); | |
| vkDestroyBuffer(device, vertexBuffer, nullptr); | |
| vkFreeMemory(device, vertexBufferMemory, nullptr); | |
| for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { | |
| vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); | |
| vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); | |
| vkDestroyFence(device, inFlightFences[i], nullptr); | |
| } | |
| vkDestroyCommandPool(device, commandPool, nullptr); | |
| vkDestroyDevice(device, nullptr); | |
| if (enableValidationLayers) { | |
| DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); | |
| } | |
| vkDestroySurfaceKHR(instance, surface, nullptr); | |
| vkDestroyInstance(instance, nullptr); | |
| glfwDestroyWindow(window); | |
| glfwTerminate(); | |
| } | |
| void recreateSwapChain() { | |
| int width = 0, height = 0; | |
| glfwGetFramebufferSize(window, &width, &height); | |
| while (width == 0 || height == 0) { | |
| glfwGetFramebufferSize(window, &width, &height); | |
| glfwWaitEvents(); | |
| } | |
| vkDeviceWaitIdle(device); | |
| cleanupSwapChain(); | |
| createSwapChain(); | |
| createImageViews(); | |
| createRenderPass(); | |
| createGraphicsPipeline(); | |
| createFramebuffers(); | |
| createCommandBuffers(); | |
| imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE); | |
| } | |
| void createInstance() { | |
| if (enableValidationLayers && !checkValidationLayerSupport()) { | |
| throw std::runtime_error("validation layers requested, but not available!"); | |
| } | |
| 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; | |
| auto extensions = getRequiredExtensions(); | |
| createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); | |
| createInfo.ppEnabledExtensionNames = extensions.data(); | |
| VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; | |
| if (enableValidationLayers) { | |
| createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); | |
| createInfo.ppEnabledLayerNames = validationLayers.data(); | |
| populateDebugMessengerCreateInfo(debugCreateInfo); | |
| createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo; | |
| } else { | |
| createInfo.enabledLayerCount = 0; | |
| createInfo.pNext = nullptr; | |
| } | |
| if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create instance!"); | |
| } | |
| } | |
| void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) { | |
| createInfo = {}; | |
| createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; | |
| createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; | |
| createInfo.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; | |
| createInfo.pfnUserCallback = debugCallback; | |
| } | |
| void setupDebugMessenger() { | |
| if (!enableValidationLayers) return; | |
| VkDebugUtilsMessengerCreateInfoEXT createInfo; | |
| populateDebugMessengerCreateInfo(createInfo); | |
| if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to set up debug messenger!"); | |
| } | |
| } | |
| void createSurface() { | |
| if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create window surface!"); | |
| } | |
| } | |
| 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<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 a suitable GPU!"); | |
| } | |
| } | |
| void createLogicalDevice() { | |
| QueueFamilyIndices indices = findQueueFamilies(physicalDevice); | |
| std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; | |
| std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()}; | |
| float queuePriority = 1.0f; | |
| for (uint32_t 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.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()); | |
| createInfo.pQueueCreateInfos = queueCreateInfos.data(); | |
| createInfo.pEnabledFeatures = &deviceFeatures; | |
| createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()); | |
| createInfo.ppEnabledExtensionNames = deviceExtensions.data(); | |
| if (enableValidationLayers) { | |
| createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); | |
| createInfo.ppEnabledLayerNames = validationLayers.data(); | |
| } else { | |
| createInfo.enabledLayerCount = 0; | |
| } | |
| if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create logical device!"); | |
| } | |
| vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); | |
| vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); | |
| } | |
| 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[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; | |
| 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; | |
| if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != 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; | |
| } | |
| void createImageViews() { | |
| swapChainImageViews.resize(swapChainImages.size()); | |
| for (size_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]) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create image views!"); | |
| } | |
| } | |
| } | |
| void createRenderPass() { | |
| VkAttachmentDescription colorAttachment{}; | |
| colorAttachment.format = swapChainImageFormat; | |
| colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; | |
| colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; | |
| colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; | |
| colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
| colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
| colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
| colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
| VkAttachmentReference colorAttachmentRef{}; | |
| colorAttachmentRef.attachment = 0; | |
| colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| VkSubpassDescription subpass{}; | |
| subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; | |
| subpass.colorAttachmentCount = 1; | |
| subpass.pColorAttachments = &colorAttachmentRef; | |
| VkSubpassDependency dependency{}; | |
| dependency.srcSubpass = VK_SUBPASS_EXTERNAL; | |
| dependency.dstSubpass = 0; | |
| dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | |
| dependency.srcAccessMask = 0; | |
| dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; | |
| dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
| VkRenderPassCreateInfo renderPassInfo{}; | |
| renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; | |
| renderPassInfo.attachmentCount = 1; | |
| renderPassInfo.pAttachments = &colorAttachment; | |
| renderPassInfo.subpassCount = 1; | |
| renderPassInfo.pSubpasses = &subpass; | |
| renderPassInfo.dependencyCount = 1; | |
| renderPassInfo.pDependencies = &dependency; | |
| if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create render pass!"); | |
| } | |
| } | |
| void createGraphicsPipeline() { | |
| auto vertShaderCode = readFile("shaders/vert.spv"); | |
| auto fragShaderCode = readFile("shaders/frag.spv"); | |
| VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); | |
| VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); | |
| 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}; | |
| VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; | |
| vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; | |
| auto bindingDescription = Vertex::getBindingDescription(); | |
| auto attributeDescriptions = Vertex::getAttributeDescriptions(); | |
| vertexInputInfo.vertexBindingDescriptionCount = 1; | |
| vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size()); | |
| vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; | |
| vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); | |
| VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; | |
| inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | |
| inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; | |
| inputAssembly.primitiveRestartEnable = VK_FALSE; | |
| VkViewport viewport{}; | |
| viewport.x = 0.0f; | |
| viewport.y = 0.0f; | |
| viewport.width = (float) swapChainExtent.width; | |
| viewport.height = (float) swapChainExtent.height; | |
| viewport.minDepth = 0.0f; | |
| viewport.maxDepth = 1.0f; | |
| VkRect2D scissor{}; | |
| scissor.offset = {0, 0}; | |
| scissor.extent = swapChainExtent; | |
| VkPipelineViewportStateCreateInfo viewportState{}; | |
| viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; | |
| viewportState.viewportCount = 1; | |
| viewportState.pViewports = &viewport; | |
| viewportState.scissorCount = 1; | |
| viewportState.pScissors = &scissor; | |
| VkPipelineRasterizationStateCreateInfo rasterizer{}; | |
| rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | |
| rasterizer.depthClampEnable = VK_FALSE; | |
| rasterizer.rasterizerDiscardEnable = VK_FALSE; | |
| rasterizer.polygonMode = VK_POLYGON_MODE_FILL; | |
| rasterizer.lineWidth = 1.0f; | |
| rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; | |
| rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; | |
| rasterizer.depthBiasEnable = VK_FALSE; | |
| VkPipelineMultisampleStateCreateInfo multisampling{}; | |
| multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; | |
| multisampling.sampleShadingEnable = VK_FALSE; | |
| multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; | |
| VkPipelineColorBlendAttachmentState colorBlendAttachment{}; | |
| colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; | |
| colorBlendAttachment.blendEnable = VK_FALSE; | |
| VkPipelineColorBlendStateCreateInfo colorBlending{}; | |
| colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; | |
| colorBlending.logicOpEnable = VK_FALSE; | |
| colorBlending.logicOp = VK_LOGIC_OP_COPY; | |
| colorBlending.attachmentCount = 1; | |
| colorBlending.pAttachments = &colorBlendAttachment; | |
| colorBlending.blendConstants[0] = 0.0f; | |
| colorBlending.blendConstants[1] = 0.0f; | |
| colorBlending.blendConstants[2] = 0.0f; | |
| colorBlending.blendConstants[3] = 0.0f; | |
| VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; | |
| pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; | |
| pipelineLayoutInfo.setLayoutCount = 0; | |
| pipelineLayoutInfo.pushConstantRangeCount = 0; | |
| if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create pipeline layout!"); | |
| } | |
| VkGraphicsPipelineCreateInfo pipelineInfo{}; | |
| pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; | |
| pipelineInfo.stageCount = 2; | |
| pipelineInfo.pStages = shaderStages; | |
| pipelineInfo.pVertexInputState = &vertexInputInfo; | |
| pipelineInfo.pInputAssemblyState = &inputAssembly; | |
| pipelineInfo.pViewportState = &viewportState; | |
| pipelineInfo.pRasterizationState = &rasterizer; | |
| pipelineInfo.pMultisampleState = &multisampling; | |
| pipelineInfo.pColorBlendState = &colorBlending; | |
| pipelineInfo.layout = pipelineLayout; | |
| pipelineInfo.renderPass = renderPass; | |
| pipelineInfo.subpass = 0; | |
| pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; | |
| if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create graphics pipeline!"); | |
| } | |
| vkDestroyShaderModule(device, fragShaderModule, nullptr); | |
| vkDestroyShaderModule(device, vertShaderModule, nullptr); | |
| } | |
| void createFramebuffers() { | |
| swapChainFramebuffers.resize(swapChainImageViews.size()); | |
| for (size_t i = 0; i < swapChainImageViews.size(); i++) { | |
| VkImageView attachments[] = { | |
| swapChainImageViews[i] | |
| }; | |
| VkFramebufferCreateInfo framebufferInfo{}; | |
| framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; | |
| framebufferInfo.renderPass = renderPass; | |
| framebufferInfo.attachmentCount = 1; | |
| framebufferInfo.pAttachments = attachments; | |
| framebufferInfo.width = swapChainExtent.width; | |
| framebufferInfo.height = swapChainExtent.height; | |
| framebufferInfo.layers = 1; | |
| if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create framebuffer!"); | |
| } | |
| } | |
| } | |
| void createCommandPool() { | |
| QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); | |
| VkCommandPoolCreateInfo poolInfo{}; | |
| poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | |
| poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); | |
| if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create graphics command pool!"); | |
| } | |
| } | |
| void createVertexBuffer() { | |
| VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); | |
| VkBuffer stagingBuffer; | |
| VkDeviceMemory stagingBufferMemory; | |
| createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); | |
| void* data; | |
| vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data); | |
| memcpy(data, vertices.data(), (size_t) bufferSize); | |
| vkUnmapMemory(device, stagingBufferMemory); | |
| createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); | |
| copyBuffer(stagingBuffer, vertexBuffer, bufferSize); | |
| vkDestroyBuffer(device, stagingBuffer, nullptr); | |
| vkFreeMemory(device, stagingBufferMemory, nullptr); | |
| } | |
| void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) { | |
| VkBufferCreateInfo bufferInfo{}; | |
| bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; | |
| bufferInfo.size = size; | |
| bufferInfo.usage = usage; | |
| bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create buffer!"); | |
| } | |
| VkMemoryRequirements memRequirements; | |
| vkGetBufferMemoryRequirements(device, buffer, &memRequirements); | |
| VkMemoryAllocateInfo allocInfo{}; | |
| allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; | |
| allocInfo.allocationSize = memRequirements.size; | |
| allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); | |
| if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to allocate buffer memory!"); | |
| } | |
| vkBindBufferMemory(device, buffer, bufferMemory, 0); | |
| } | |
| void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { | |
| VkCommandBufferAllocateInfo allocInfo{}; | |
| allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
| allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
| allocInfo.commandPool = commandPool; | |
| allocInfo.commandBufferCount = 1; | |
| VkCommandBuffer commandBuffer; | |
| vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); | |
| VkCommandBufferBeginInfo beginInfo{}; | |
| beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
| beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; | |
| vkBeginCommandBuffer(commandBuffer, &beginInfo); | |
| VkBufferCopy copyRegion{}; | |
| copyRegion.size = size; | |
| vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); | |
| vkEndCommandBuffer(commandBuffer); | |
| VkSubmitInfo submitInfo{}; | |
| submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
| submitInfo.commandBufferCount = 1; | |
| submitInfo.pCommandBuffers = &commandBuffer; | |
| vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); | |
| vkQueueWaitIdle(graphicsQueue); | |
| vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); | |
| } | |
| uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { | |
| VkPhysicalDeviceMemoryProperties memProperties; | |
| vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); | |
| for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { | |
| if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { | |
| return i; | |
| } | |
| } | |
| throw std::runtime_error("failed to find suitable memory type!"); | |
| } | |
| void createCommandBuffers() { | |
| commandBuffers.resize(swapChainFramebuffers.size()); | |
| VkCommandBufferAllocateInfo allocInfo{}; | |
| allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
| allocInfo.commandPool = commandPool; | |
| allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
| allocInfo.commandBufferCount = (uint32_t) commandBuffers.size(); | |
| if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to allocate command buffers!"); | |
| } | |
| for (size_t i = 0; i < commandBuffers.size(); i++) { | |
| VkCommandBufferBeginInfo beginInfo{}; | |
| beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
| if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to begin recording command buffer!"); | |
| } | |
| VkRenderPassBeginInfo renderPassInfo{}; | |
| renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; | |
| renderPassInfo.renderPass = renderPass; | |
| renderPassInfo.framebuffer = swapChainFramebuffers[i]; | |
| renderPassInfo.renderArea.offset = {0, 0}; | |
| renderPassInfo.renderArea.extent = swapChainExtent; | |
| VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; | |
| renderPassInfo.clearValueCount = 1; | |
| renderPassInfo.pClearValues = &clearColor; | |
| vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); | |
| vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); | |
| VkBuffer vertexBuffers[] = {vertexBuffer}; | |
| VkDeviceSize offsets[] = {0}; | |
| vkCmdBindVertexBuffers(commandBuffers[i], 0, 1, vertexBuffers, offsets); | |
| vkCmdDraw(commandBuffers[i], static_cast<uint32_t>(vertices.size()), 1, 0, 0); | |
| vkCmdEndRenderPass(commandBuffers[i]); | |
| if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to record command buffer!"); | |
| } | |
| } | |
| } | |
| void createSyncObjects() { | |
| imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); | |
| renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); | |
| inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); | |
| imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE); | |
| VkSemaphoreCreateInfo semaphoreInfo{}; | |
| semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
| VkFenceCreateInfo fenceInfo{}; | |
| fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; | |
| fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; | |
| for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { | |
| if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || | |
| vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || | |
| vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create synchronization objects for a frame!"); | |
| } | |
| } | |
| } | |
| void drawFrame() { | |
| vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); | |
| uint32_t imageIndex; | |
| VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); | |
| if (result == VK_ERROR_OUT_OF_DATE_KHR) { | |
| recreateSwapChain(); | |
| return; | |
| } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { | |
| throw std::runtime_error("failed to acquire swap chain image!"); | |
| } | |
| if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { | |
| vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX); | |
| } | |
| imagesInFlight[imageIndex] = inFlightFences[currentFrame]; | |
| VkSubmitInfo submitInfo{}; | |
| submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
| VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; | |
| VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; | |
| submitInfo.waitSemaphoreCount = 1; | |
| submitInfo.pWaitSemaphores = waitSemaphores; | |
| submitInfo.pWaitDstStageMask = waitStages; | |
| submitInfo.commandBufferCount = 1; | |
| submitInfo.pCommandBuffers = &commandBuffers[imageIndex]; | |
| VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; | |
| submitInfo.signalSemaphoreCount = 1; | |
| submitInfo.pSignalSemaphores = signalSemaphores; | |
| vkResetFences(device, 1, &inFlightFences[currentFrame]); | |
| if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to submit draw command buffer!"); | |
| } | |
| VkPresentInfoKHR presentInfo{}; | |
| presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; | |
| presentInfo.waitSemaphoreCount = 1; | |
| presentInfo.pWaitSemaphores = signalSemaphores; | |
| VkSwapchainKHR swapChains[] = {swapChain}; | |
| presentInfo.swapchainCount = 1; | |
| presentInfo.pSwapchains = swapChains; | |
| presentInfo.pImageIndices = &imageIndex; | |
| result = vkQueuePresentKHR(presentQueue, &presentInfo); | |
| if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { | |
| framebufferResized = false; | |
| recreateSwapChain(); | |
| } else if (result != VK_SUCCESS) { | |
| throw std::runtime_error("failed to present swap chain image!"); | |
| } | |
| currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; | |
| } | |
| VkShaderModule createShaderModule(const std::vector<char>& code) { | |
| VkShaderModuleCreateInfo createInfo{}; | |
| createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; | |
| createInfo.codeSize = code.size(); | |
| createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data()); | |
| VkShaderModule shaderModule; | |
| if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create shader module!"); | |
| } | |
| return shaderModule; | |
| } | |
| VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) { | |
| for (const auto& availableFormat : availableFormats) { | |
| if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && 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 != UINT32_MAX) { | |
| return capabilities.currentExtent; | |
| } else { | |
| int width, height; | |
| glfwGetFramebufferSize(window, &width, &height); | |
| VkExtent2D actualExtent = { | |
| static_cast<uint32_t>(width), | |
| static_cast<uint32_t>(height) | |
| }; | |
| actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); | |
| actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); | |
| return actualExtent; | |
| } | |
| } | |
| 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; | |
| } | |
| bool isDeviceSuitable(VkPhysicalDevice device) { | |
| QueueFamilyIndices indices = findQueueFamilies(device); | |
| bool extensionsSupported = checkDeviceExtensionSupport(device); | |
| bool swapChainAdequate = false; | |
| if (extensionsSupported) { | |
| SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); | |
| swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); | |
| } | |
| return indices.isComplete() && extensionsSupported && swapChainAdequate; | |
| } | |
| 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.queueFlags & VK_QUEUE_GRAPHICS_BIT) { | |
| indices.graphicsFamily = i; | |
| } | |
| VkBool32 presentSupport = false; | |
| vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); | |
| if (presentSupport) { | |
| indices.presentFamily = i; | |
| } | |
| if (indices.isComplete()) { | |
| break; | |
| } | |
| i++; | |
| } | |
| return indices; | |
| } | |
| std::vector<const char*> getRequiredExtensions() { | |
| uint32_t glfwExtensionCount = 0; | |
| const char** glfwExtensions; | |
| glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); | |
| std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); | |
| if (enableValidationLayers) { | |
| extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | |
| } | |
| return extensions; | |
| } | |
| 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 true; | |
| } | |
| static std::vector<char> readFile(const std::string& filename) { | |
| std::ifstream file(filename, std::ios::ate | std::ios::binary); | |
| if (!file.is_open()) { | |
| throw std::runtime_error("failed to open file!"); | |
| } | |
| size_t fileSize = (size_t) file.tellg(); | |
| std::vector<char> buffer(fileSize); | |
| file.seekg(0); | |
| file.read(buffer.data(), fileSize); | |
| file.close(); | |
| return buffer; | |
| } | |
| static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { | |
| std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; | |
| return VK_FALSE; | |
| } | |
| }; | |
| int main() { | |
| HelloTriangleApplication app; | |
| try { | |
| app.run(); | |
| } catch (const std::exception& e) { | |
| std::cerr << e.what() << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| return EXIT_SUCCESS; | |
| } |