diff --git a/Makefile.conf b/Makefile.conf index cf83902..ee8e5d0 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -31,7 +31,8 @@ CXXFLAGS+=\ -fPIC \ -Wall \ -Wextra \ - -pedantic + -pedantic \ + -Wunused-function ifneq (,$(findstring g++,$(CXX))) COMPILE_FLAGS+=-fdiagnostics-color=always @@ -45,6 +46,7 @@ LIBS+=-lm LIBS+=-lglfw LIBS+=-lVulkan LIBS+=-lglm_shared +LIBS+=-lheck_log LDFLAGS+=$(LIBS) diff --git a/shaders/base.frag b/shaders/base.frag new file mode 100644 index 0000000..7c5b0e7 --- /dev/null +++ b/shaders/base.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} diff --git a/shaders/base.vert b/shaders/base.vert new file mode 100644 index 0000000..f5b2f8d --- /dev/null +++ b/shaders/base.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} diff --git a/src/main_base.cc b/src/main_base.cc index b6dde92..4bbd94e 100644 --- a/src/main_base.cc +++ b/src/main_base.cc @@ -1,101 +1,901 @@ #define GLFW_INCLUDE_VULKAN #include - +#include #include #include #include #include +#include +#include +#include + const uint32_t WIDTH = 800; const uint32_t HEIGHT = 600; -class HelloTriangleApplication { -public: - void run() - { - initWindow(); - initVulkan(); - mainLoop(); - cleanup(); - } +namespace Heck { + struct VulkanBase { + VulkanBase(GLFWwindow& window) : window(window) + { + deviceExtensions.emplace_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + } + VulkanBase() = delete; + VulkanBase(VulkanBase& other) = delete; + VulkanBase(VulkanBase&& other) = delete; + VulkanBase& operator=(VulkanBase& rhs) = delete; + VulkanBase& operator=(VulkanBase&& rhs) = delete; + ~VulkanBase() = default; -private: - GLFWwindow* window; - VkInstance instance; + struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; - void initWindow() - { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); - } + bool isComplete() + { + HECK_LOG_INFO(""); + return graphicsFamily.has_value() && presentFamily.has_value(); + } + }; - void initVulkan() - { - createInstance(); - } + struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + + std::vector deviceExtensions{}; + + GLFWwindow& window; + VkInstance instance{}; + VkSurfaceKHR surface{}; + + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkDevice device{}; + + VkQueue graphicsQueue{}; + VkQueue presentQueue{}; + + VkSwapchainKHR swapChain{}; + std::vector swapChainImages{}; + VkFormat swapChainImageFormat{}; + VkExtent2D swapChainExtent{}; + std::vector swapChainImageViews{}; + + VkPipelineLayout pipelineLayout{}; + VkRenderPass renderPass{}; + VkPipeline graphicsPipeline{}; + std::vector swapChainFramebuffers{}; - void mainLoop() - { - while (!glfwWindowShouldClose(window)) { - glfwPollEvents(); + VkCommandPool commandPool{}; + VkCommandBuffer commandBuffer{}; + + VkSemaphore imageAvailableSemaphore{}; + VkSemaphore renderFinishedSemaphore{}; + VkFence inFlightFence{}; + + + void init() + { + HECK_LOG_INFO(""); + createInstance(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); + createSwapChain(); + createImageViews(); + createRenderPass(); + createGraphicsPipeline(); + createFramebuffers(); + createCommandPool(); + createCommandBuffer(); + createSyncObjects(); } - } - void cleanup() - { - vkDestroyInstance(instance, nullptr); - glfwDestroyWindow(window); - glfwTerminate(); - } + void createInstance() + { + HECK_LOG_INFO(""); + VkInstanceCreateInfo createInfo{}; + + 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; + createInfo.pApplicationInfo = &appInfo; + + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + createInfo.enabledExtensionCount = glfwExtensionCount; + createInfo.ppEnabledExtensionNames = glfwExtensions; + createInfo.enabledLayerCount = 0; + + std::vector requiredExtensions{}; + for (uint32_t i = 0; i < glfwExtensionCount; i++) { + requiredExtensions.emplace_back(glfwExtensions[i]); + } + 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(); + + if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { + throw std::runtime_error("failed to create instance!"); + } + } + + void createSurface() + { + HECK_LOG_INFO(""); + if (glfwCreateWindowSurface(instance, &window, nullptr, &surface) != VK_SUCCESS) { + throw std::runtime_error("failed to create window surface!"); + } + } + + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) + { + HECK_LOG_INFO(""); + 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) { + HECK_LOG_INFO("Checking queue family nr:" << 0); + 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; + } + + void pickPhysicalDevice() + { + HECK_LOG_INFO(""); + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + HECK_LOG_INFO("devices found:" << deviceCount); + 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 VkPhysicalDevice& 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() + { + HECK_LOG_INFO(""); + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + + std::vector queueCreateInfos; + std::set 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(queueCreateInfos.size()); + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + + createInfo.pEnabledFeatures = &deviceFeatures; - 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; + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); - VkInstanceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pApplicationInfo = &appInfo; + createInfo.enabledLayerCount = 0; - uint32_t glfwExtensionCount = 0; - const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - createInfo.enabledExtensionCount = glfwExtensionCount; - createInfo.ppEnabledExtensionNames = glfwExtensions; - createInfo.enabledLayerCount = 0; + if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { + throw std::runtime_error("failed to create logical device!"); + } - std::vector requiredExtensions{}; - for (uint32_t i = 0; i < glfwExtensionCount; i++) { - requiredExtensions.emplace_back(glfwExtensions[i]); + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); } - 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(); + bool isDeviceSuitable(VkPhysicalDevice device) + { + HECK_LOG_INFO(device); + bool ret = false; + QueueFamilyIndices indices = findQueueFamilies(device); - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { - throw std::runtime_error("failed to create instance!"); + bool extensionsSupported = checkDeviceExtensionSupport(device); + bool swapChainAdequate = false; + if (extensionsSupported) { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); + swapChainAdequate = !swapChainSupport.formats.empty() && + !swapChainSupport.presentModes.empty(); + } + + if (indices.isComplete() && extensionsSupported && swapChainAdequate) { + HECK_LOG_INFO("device is suitable"); + ret = true; + } else { + HECK_LOG_INFO("device not suitable"); + ret = false; + } + return ret; + } + + + void createSwapChain() + { + HECK_LOG_INFO(""); + 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; + + createInfo.oldSwapchain = VK_NULL_HANDLE; + + 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; + } + + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) + { + HECK_LOG_INFO(""); + 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& availablePresentModes) + { + HECK_LOG_INFO(""); + 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) + { + HECK_LOG_INFO(""); + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + int width; + int height; + glfwGetFramebufferSize(&window, &width, &height); + + VkExtent2D actualExtent = { static_cast(width), + static_cast(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; + } + } + + bool checkDeviceExtensionSupport(VkPhysicalDevice device) + { + HECK_LOG_INFO(""); + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties( + device, + nullptr, + &extensionCount, + availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); + } + + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) + { + HECK_LOG_INFO(""); + 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 createImageViews() + { + HECK_LOG_INFO(""); + swapChainImageViews.resize(swapChainImages.size()); + + for (size_t i = 0; i < swapChainImages.size(); i++) { + HECK_LOG_INFO("Creating swap chain image nr: " << 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 createGraphicsPipeline() + { + HECK_LOG_INFO(""); + auto vertShaderCode = readFile("../shaders/base.vert.spv"); + auto fragShaderCode = readFile("../shaders/base.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; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkPipelineViewportStateCreateInfo viewportState{}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; + + 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; + + std::vector dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState{}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = static_cast(dynamicStates.size()); + dynamicState.pDynamicStates = dynamicStates.data(); + + 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.pDynamicState = &dynamicState; + 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); + } + + VkShaderModule createShaderModule(const std::vector& code) + { + HECK_LOG_INFO(""); + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + throw std::runtime_error("failed to create shader module!"); + } + + return shaderModule; + } + + void createRenderPass() + { + HECK_LOG_INFO(""); + 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 createFramebuffers() + { + HECK_LOG_INFO(""); + 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() + { + HECK_LOG_INFO(""); + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create command pool!"); + } + } + + void createCommandBuffer() + { + HECK_LOG_INFO(""); + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = 1; + + if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate command buffers!"); + } + } + + void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) + { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + if (vkBeginCommandBuffer(commandBuffer, &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[imageIndex]; + 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(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(swapChainExtent.width); + viewport.height = static_cast(swapChainExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = { 0, 0 }; + scissor.extent = swapChainExtent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + throw std::runtime_error("failed to record command buffer!"); + } + } + + void createSyncObjects() + { + 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; + + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != + VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != + VK_SUCCESS || + vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence) != VK_SUCCESS) { + throw std::runtime_error("failed to create synchronization objects for a frame!"); + } + } + + void drawFrame() + { + vkWaitForFences(device, 1, &inFlightFence, VK_TRUE, UINT64_MAX); + vkResetFences(device, 1, &inFlightFence); + + uint32_t imageIndex; + vkAcquireNextImageKHR( + device, + swapChain, + UINT64_MAX, + imageAvailableSemaphore, + VK_NULL_HANDLE, + &imageIndex); + + vkResetCommandBuffer(commandBuffer, /*VkCommandBufferResetFlagBits*/ 0); + recordCommandBuffer(commandBuffer, imageIndex); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = { imageAvailableSemaphore }; + VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + VkSemaphore signalSemaphores[] = { renderFinishedSemaphore }; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFence) != 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; + + vkQueuePresentKHR(presentQueue, &presentInfo); + } + + void cleanup() + { + HECK_LOG_INFO(""); + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + vkDestroyFence(device, inFlightFence, nullptr); + + vkDestroyCommandPool(device, commandPool, nullptr); + + for (auto framebuffer : swapChainFramebuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + } + + static std::vector readFile(const std::string& filename) + { + HECK_LOG_INFO(""); + 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 buffer(fileSize); + file.seekg(0); + file.read(buffer.data(), fileSize); + file.close(); + + return buffer; + } + }; +} // namespace Heck + + +GLFWwindow* window = nullptr; +Heck::VulkanBase *vb = nullptr; + +void init() +{ + HECK_LOG_INFO(""); + glfwInit(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); +} + +void mainLoop() +{ + HECK_LOG_INFO(""); + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + vb->drawFrame(); + } -}; + + vkDeviceWaitIdle(vb->device); +} + +void cleanup() +{ + HECK_LOG_INFO(""); + glfwDestroyWindow(window); + glfwTerminate(); +} int main() { - HelloTriangleApplication app{}; + Heck::Log::set_level(HECK_LOG_LEVEL_ALL); + init(); - try { - app.run(); - } catch (const std::exception& e) { - std::cerr << e.what() << std::endl; - return EXIT_FAILURE; - } + Heck::VulkanBase _vb{ *window }; + vb = &_vb; + vb->init(); + + mainLoop(); + + vb->cleanup(); + cleanup(); return EXIT_SUCCESS; }