diff --git a/.gitignore b/.gitignore index a5dec1c..592c69c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ .vs/ cmake-build*/ out/ +build/ +*.spv +compile_commands.json diff --git a/.gitmodules b/.gitmodules index bcff14d..33a4244 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "Libraries/glfw"] path = Libraries/glfw url = https://github.com/glfw/glfw +[submodule "Libraries/spdlog"] + path = Libraries/spdlog + url = https://github.com/gabime/spdlog diff --git a/CMakeLists.txt b/CMakeLists.txt index e697264..8635b38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,11 +4,20 @@ project(VulkanTutorial) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -file(GLOB SOURCE_CODE ./src/*.cpp) +file(GLOB_RECURSE SOURCE_CODE ./src/**.cpp) add_executable(${PROJECT_NAME} ${SOURCE_CODE} ) +file(GLOB SHADER_CODE ./shaders/*.spv) +file(COPY ${SHADER_CODE} DESTINATION ${CMAKE_BINARY_DIR}) + + +target_include_directories(${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src +) + # 1. 查找 Vulkan find_package(Vulkan REQUIRED) if(NOT Vulkan_FOUND) @@ -22,10 +31,12 @@ set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) add_subdirectory(Libraries/glfw) # GLFW add_subdirectory(Libraries/glm) # GLM +add_subdirectory(Libraries/spdlog) # 链接依赖 target_link_libraries(${PROJECT_NAME} PRIVATE - glfw # glfw 的 target 名称(由 add_subdirectory 提供) - Vulkan::Vulkan # 来自 find_package(Vulkan) + glfw + Vulkan::Vulkan + spdlog ) diff --git a/Libraries/spdlog b/Libraries/spdlog new file mode 160000 index 0000000..f5f173a --- /dev/null +++ b/Libraries/spdlog @@ -0,0 +1 @@ +Subproject commit f5f173a1a57d0e2e0115f2ed71ee7ea316516853 diff --git a/shaders/triangle.frag b/shaders/triangle.frag new file mode 100644 index 0000000..f58a9ce --- /dev/null +++ b/shaders/triangle.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/triangle.vert b/shaders/triangle.vert new file mode 100644 index 0000000..2854cbc --- /dev/null +++ b/shaders/triangle.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/Application/Application.cpp b/src/Application/Application.cpp new file mode 100644 index 0000000..273f9d5 --- /dev/null +++ b/src/Application/Application.cpp @@ -0,0 +1,1041 @@ +#include "Application.h" +#include "GLFW/glfw3.h" +#include "Log/Log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Utils { + +// 通过自定义得分测试,选出最佳设备 +int RateDeviceSuitablility(VkPhysicalDevice device) { + VkPhysicalDeviceProperties deviceProperties; + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceProperties(device, &deviceProperties); + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + + int score = 0; + + // 优先GPU + switch (deviceProperties.deviceType) { + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { + score += 1000; + break; + } + case VK_PHYSICAL_DEVICE_TYPE_CPU: { + score += 100; + } + case VK_PHYSICAL_DEVICE_TYPE_OTHER: + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + case VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM: + break; + } + + score += deviceProperties.limits.maxImageDimension2D; + + if (!deviceFeatures.geometryShader) { + // 需要设备必须支持 geometryshader feature + return 0; + } + + return score; +} + +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); + } +} + +} // namespace Utils + +void Application::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; +} + +std::vector Application::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 buffer(fileSize); + + file.seekg(0); + file.read(buffer.data(), fileSize); + + file.close(); + + return buffer; +} + +void Application::run() { + InitWindow(); + InitVulkan(); + + MainLoop(); + + CleanUp(); +} + +void Application::InitWindow() { + glfwInit(); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + m_Window = glfwCreateWindow(m_WIDTH, m_HEIGHT, "Vulkan window", nullptr, nullptr); + + if (!m_Window) { + LOG_ERROR("glfw Window Create Error"); + throw std::error_code(); + } + + glfwSetWindowUserPointer(m_Window, this); + + glfwSetKeyCallback(m_Window, KeyCallback); + glfwSetFramebufferSizeCallback(m_Window, FramebufferCallback); +} + +void Application::InitVulkan() { + CreateInstance(); + SetupDebugMessenger(); + CreateSurface(); + PickPhysicalDevice(); + CreateLogicalDevice(); + CreateSwapChain(); + CreateImageViews(); + CreateRenderPass(); + CreateGraphicsPipeline(); + CreateFramebuffers(); + CreateCommandPool(); + CreateCommandBuffers(); + CreateSyncObjects(); +} + +void Application::CreateLogicalDevice() { + LOG_INFO("Try Create Logical Device..."); + QueueFamilyIndices indices = FindQueueFamilies(m_PhysicalDevice); + + std::vector queueCreateInfos; + std::set uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()}; + + // 优先级分配 [0.0, 1.0], 影响命令缓冲区的执行调度 + 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; + + // 拓展添加 + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + + if (vkCreateDevice(m_PhysicalDevice, &createInfo, nullptr, &m_Device)) { + throw std::runtime_error("failed to create logical device"); + } + + vkGetDeviceQueue(m_Device, indices.graphicsFamily.value(), 0, &m_GraphicsQueue); + vkGetDeviceQueue(m_Device, indices.presentFamily.value(), 0, &m_PresentQueue); + + LOG_INFO("Create Logical Device Success"); +} + +void Application::CreateSwapChain() { + LOG_INFO("Try Create Vulkan SwapChain ..."); + SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(m_PhysicalDevice); + + VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats); + + VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes); + + VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities); + + // 交换链拥有多少个图像,'+1',坚持此最小值意味着我们有时可能必须等待驱动程序完成内部操作,然后才能获取另一个图像进行渲染。 因此,建议请求至少比最小值多一个图像 + 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 = m_Surface; + + createInfo.minImageCount = imageCount; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = FindQueueFamilies(m_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.queueFamilyIndexCount = 0; // Optional + createInfo.pQueueFamilyIndices = nullptr; + } + + 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; + + VkSwapchainKHR swapChain; + + if (vkCreateSwapchainKHR(m_Device, &createInfo, nullptr, &m_SwapChain) != VK_SUCCESS) { + throw std::runtime_error("failed to create swap chain"); + } + + LOG_INFO("Create Vulkan SwapChain Successs"); + + vkGetSwapchainImagesKHR(m_Device, m_SwapChain, &imageCount, nullptr); + m_SwapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(m_Device, m_SwapChain, &imageCount, m_SwapChainImages.data()); + + // 保存 图形选择的格式和范围 + m_SwapChainImageFormat = surfaceFormat.format; + m_SwapChainExtent = extent; +} + +void Application::CreateImageViews() { + m_SwapChainImageViews.resize(m_SwapChainImages.size()); + + for (size_t i = 0; i < m_SwapChainImages.size(); i++) { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = m_SwapChainImages[i]; + + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_SwapChainImageFormat; + + // components 允许字体对颜色混合 + 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; + + // subresourceRange 描述图像的用途以及应访问图像的哪一部分,此处使用图像作为颜色目标,没有任何的mipmap级别或多个图层 + 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(m_Device, &createInfo, nullptr, &m_SwapChainImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("tailed to create image views"); + } + } +} + +void Application::CreateRenderPass() { + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = m_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; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + + 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; + + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(m_Device, &renderPassInfo, nullptr, &m_RenderPass) != VK_SUCCESS) { + throw std::runtime_error("Failed to create Render Pass!"); + } +} + +void Application::CreateGraphicsPipeline() { + auto vertShaderCode = ReadFile("vert.spv"); + auto fragShaderCode = ReadFile("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.pVertexBindingDescriptions = nullptr; // Optional + vertexInputInfo.vertexAttributeDescriptionCount = 0; + vertexInputInfo.pVertexAttributeDescriptions = nullptr; // Optional + + 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)m_SwapChainExtent.width; + viewport.height = (float)m_SwapChainExtent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = m_SwapChainExtent; + + 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(); + + 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; + rasterizer.depthBiasConstantFactor = 0.0f; // Optional + rasterizer.depthBiasClamp = 0.0f; // Optional + rasterizer.depthBiasSlopeFactor = 0.0f; // Optional + + // 多采样 + VkPipelineMultisampleStateCreateInfo multisampling{}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + // 颜色混合 + 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_TRUE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + VkPipelineColorBlendStateCreateInfo colorBlending{}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; // Optional + colorBlending.blendConstants[1] = 0.0f; // Optional + colorBlending.blendConstants[2] = 0.0f; // Optional + colorBlending.blendConstants[3] = 0.0f; // Optional + + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{}; + pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCreateInfo.setLayoutCount = 0; + pipelineLayoutCreateInfo.pSetLayouts = nullptr; + pipelineLayoutCreateInfo.pushConstantRangeCount = 0; + pipelineLayoutCreateInfo.pPushConstantRanges = nullptr; + + if (vkCreatePipelineLayout(m_Device, &pipelineLayoutCreateInfo, nullptr, &m_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.pDepthStencilState = nullptr; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + + pipelineInfo.layout = m_PipelineLayout; + pipelineInfo.renderPass = m_RenderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + + if (vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_GraphicsPipeline) != VK_SUCCESS) { + throw std::runtime_error("failed to create graphic pipeline!"); + } + + vkDestroyShaderModule(m_Device, fragShaderModule, nullptr); + vkDestroyShaderModule(m_Device, vertShaderModule, nullptr); +} + +void Application::CreateFramebuffers() { + m_SwapChainFramebuffers.resize(m_SwapChainImageViews.size()); + for (size_t i = 0; i < m_SwapChainImageViews.size(); i++) { + VkImageView attachments[] = { + m_SwapChainImageViews[i]}; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = m_RenderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = m_SwapChainExtent.width; + framebufferInfo.height = m_SwapChainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(m_Device, &framebufferInfo, nullptr, &m_SwapChainFramebuffers[i]) != VK_SUCCESS) { + + throw std::runtime_error("failed to create framebuffer"); + } + } +} + +void Application::CreateCommandPool() { + QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(m_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(m_Device, &poolInfo, nullptr, &m_CommandPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create command pool!"); + } +} + +void Application::CreateCommandBuffers() { + m_CommandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = m_CommandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t)m_CommandBuffers.size(); + + if (vkAllocateCommandBuffers(m_Device, &allocInfo, m_CommandBuffers.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate command buffers!"); + } +} + +void Application::RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + + 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 = m_RenderPass; + renderPassInfo.framebuffer = m_SwapChainFramebuffers[imageIndex]; + + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = m_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, m_GraphicsPipeline); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(m_SwapChainExtent.width); + viewport.height = static_cast(m_SwapChainExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = m_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 commend buffer!"); + } +} + +VkShaderModule Application::CreateShaderModule(const std::vector &code) { + 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(m_Device, &createInfo, nullptr, &shaderModule)) { + throw std::runtime_error("failed to create shader module!"); + } + + return shaderModule; +} + +void Application::MainLoop() { + while (!glfwWindowShouldClose(m_Window)) { + glfwPollEvents(); + DrawFrame(); + } + + vkDeviceWaitIdle(m_Device); +} + +void Application::DrawFrame() { + vkWaitForFences(m_Device, 1, &m_InFlightFences[m_CurrrentFrame], VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + VkResult result = vkAcquireNextImageKHR(m_Device, m_SwapChain, UINT64_MAX, m_ImageAvailableSemaphores[m_CurrrentFrame], 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!"); + } + + vkResetFences(m_Device, 1, &m_InFlightFences[m_CurrrentFrame]); + + vkResetCommandBuffer(m_CommandBuffers[m_CurrrentFrame], 0); + RecordCommandBuffer(m_CommandBuffers[m_CurrrentFrame], imageIndex); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphore[] = {m_ImageAvailableSemaphores[m_CurrrentFrame]}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphore; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &m_CommandBuffers[m_CurrrentFrame]; + + VkSemaphore signalSemaphore[] = {m_RenderFinishedSemaphores[m_CurrrentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphore; + + if (vkQueueSubmit(m_GraphicsQueue, 1, &submitInfo, m_InFlightFences[m_CurrrentFrame]) != VK_SUCCESS) { + throw std::runtime_error("failed to submitInfo draw command buffer!"); + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphore; + + VkSwapchainKHR swapChains[] = {m_SwapChain}; + + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + + result = vkQueuePresentKHR(m_PresentQueue, &presentInfo); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + m_FramebufferResized = false; + ReCreateSwapChain(); + } else if (result != VK_SUCCESS) { + throw std::runtime_error("failed to presetn swap chain image!"); + } + + m_CurrrentFrame = (m_CurrrentFrame + 1) % MAX_FRAMES_IN_FLIGHT; +} + +void Application::CreateSyncObjects() { + m_ImageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + m_RenderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + m_InFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + + 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(m_Device, &semaphoreInfo, nullptr, &m_ImageAvailableSemaphores[i]) != VK_SUCCESS || + vkCreateSemaphore(m_Device, &semaphoreInfo, nullptr, &m_RenderFinishedSemaphores[i]) != VK_SUCCESS || + vkCreateFence(m_Device, &fenceInfo, nullptr, &m_InFlightFences[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create semaphores!"); + } + } +} + +void Application::CleanUpSwapChain() { + for (auto framebuffer : m_SwapChainFramebuffers) { + vkDestroyFramebuffer(m_Device, framebuffer, nullptr); + } + for (auto imageView : m_SwapChainImageViews) { + vkDestroyImageView(m_Device, imageView, nullptr); + } + + vkDestroySwapchainKHR(m_Device, m_SwapChain, nullptr); +} + +void Application::ReCreateSwapChain() { + int width = 0, height = 0; + glfwGetFramebufferSize(m_Window, &width, &height); + while (width == 0 || height == 0) { + glfwGetFramebufferSize(m_Window, &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(m_Device); + + CleanUpSwapChain(); + + CreateSwapChain(); + CreateImageViews(); + CreateFramebuffers(); +} + +void Application::CleanUp() { + CleanUpSwapChain(); + + vkDestroyPipeline(m_Device, m_GraphicsPipeline, nullptr); + vkDestroyPipelineLayout(m_Device, m_PipelineLayout, nullptr); + vkDestroyRenderPass(m_Device, m_RenderPass, nullptr); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(m_Device, m_ImageAvailableSemaphores[i], nullptr); + vkDestroySemaphore(m_Device, m_RenderFinishedSemaphores[i], nullptr); + vkDestroyFence(m_Device, m_InFlightFences[i], nullptr); + } + + vkDestroyCommandPool(m_Device, m_CommandPool, nullptr); + vkDestroyDevice(m_Device, nullptr); + + if (enableValidationLayer) { + Utils::DestroyDebugUtilsMessengerEXT(m_Instance, m_DebugMessager, nullptr); + } + + vkDestroySurfaceKHR(m_Instance, m_Surface, nullptr); + vkDestroyInstance(m_Instance, nullptr); + + glfwDestroyWindow(m_Window); + + glfwTerminate(); +} + +void Application::CreateInstance() { + LOG_INFO("Try create Vulkan Instance..."); + + if (enableValidationLayer && !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.apiVersion = VK_API_VERSION_1_0; + + // 创建VkInstance + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + + // 通过 glfw 获取拓展来与窗口系统交互 + + /* + uint32_t glfwExtensionCount = 0; + const char **glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + */ + + auto extensions = getRequiredExtensions(); + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + // 创建 debug调试信息 + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; + + if (enableValidationLayer) { + createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.ppEnabledLayerNames = validationLayers.data(); + + PopulateDebugMessengerCreateInfo(debugCreateInfo); + createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo; + + } else { + createInfo.enabledLayerCount = 0; + createInfo.pNext = nullptr; + } + + // 创建Vulkan Instance + VkResult result = vkCreateInstance(&createInfo, nullptr, &m_Instance); + if (result != VK_SUCCESS) { + throw std::runtime_error("failed to create instance!"); + } + + LOG_INFO("Create Vulkan Instance Success"); +} + +bool Application::IsDeviceSuitable(VkPhysicalDevice device) { + /* + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(device, &deviceProperties); + + // 查询对纹理压缩 64 位浮点数和多视口等信息 + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + + return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && deviceFeatures.geometryShader; + */ + + 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; +} + +QueueFamilyIndices Application::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; + } + + VkBool32 presentSUpport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, m_Surface, &presentSUpport); + + if (presentSUpport) { + indices.presentFamily = i; + } + + if (indices.IsComplete()) + break; + + i++; + } + + return indices; +} + +VkSurfaceFormatKHR Application::ChooseSwapSurfaceFormat(const std::vector &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 Application::ChooseSwapPresentMode(const std::vector &availablePresentModes) { + for (const auto &availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + return availablePresentMode; + } + } + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D Application::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + int width, height; + glfwGetFramebufferSize(m_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; + } +} + +SwapChainSupportDetails Application::QuerySwapChainSupport(VkPhysicalDevice device) { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_Surface, &details.capabilities); + + // 查询表面支持格式 + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_Surface, &formatCount, nullptr); + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_Surface, &formatCount, details.formats.data()); + } + + // 查询支持的演示模式的工作方式与 vkGetPhysicalDeviceSurfacePresentModesKHR + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, m_Surface, &presentModeCount, nullptr); + if (presentModeCount != 0) { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, m_Surface, &presentModeCount, details.presentModes.data()); + } + + return details; +} + +bool Application::CheckValidationLayerSupport() { + uint32_t layerCount = 0; + 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; +} + +bool Application::CheckDeviceExtensionSupport(VkPhysicalDevice device) { + 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(); +} + +std::vector Application::getRequiredExtensions() { + uint32_t glfwExtensionCount = 0; + const char **glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); + if (enableValidationLayer) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + return extensions; +} + +void Application::CreateSurface() { + LOG_INFO("Try Create Vulkan Surface..."); + if (glfwCreateWindowSurface(m_Instance, m_Window, nullptr, &m_Surface)) { + throw std::runtime_error("failed to create window surface"); + } + + LOG_INFO("Create Vulkan Surface Success"); +} + +void Application::SetupDebugMessenger() { + if (!enableValidationLayer) + return; + + LOG_INFO("Try set up Debug Messenger..."); + + VkDebugUtilsMessengerCreateInfoEXT 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; + + if (Utils::CreateDebugUtilsMessengerEXT(m_Instance, &createInfo, nullptr, &m_DebugMessager) != VK_SUCCESS) { + throw std::runtime_error("Failed to set up debug messenger!"); + } + + LOG_INFO("Setup Debug Messenger Success"); +} + +void Application::PickPhysicalDevice() { + LOG_INFO("Try pick Physic Device ..."); + + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(m_Instance, &deviceCount, nullptr); + + if (deviceCount == 0) { + throw std::runtime_error("Failed to find GPUs witd Vulkan support!"); + } + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(m_Instance, &deviceCount, devices.data()); + + std::multimap candidates; + for (const auto &device : devices) { + int score = Utils::RateDeviceSuitablility(device); + candidates.insert(std::make_pair(score, device)); + + VkPhysicalDeviceProperties physicalDeviceProperties; + vkGetPhysicalDeviceProperties(device, &physicalDeviceProperties); + LOG_TRACE("Score: {0}\tDevice: {1}", score, physicalDeviceProperties.deviceName); + + /* + if (IsDeviceSuitable(device)) { + m_PhysicalDevice = device; + break; + } + */ + } + + if (candidates.rbegin()->first > 0) { + m_PhysicalDevice = candidates.rbegin()->second; + } else { + throw std::runtime_error("failed to find a suitable GPU"); + } +} + +VKAPI_ATTR VkBool32 VKAPI_CALL Application::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) { + LOG_TRACE("Validation Layer: {0}", pCallbackData->pMessage); + return VK_FALSE; +} + +void Application::KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { + LOG_TRACE("Key: {0}\tScancode: {1}\tAction: {2}\tMods: {3}\n", key, scancode, action, mods); +} + +void Application::FramebufferCallback(GLFWwindow *window, int width, int height) { + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); + app->m_FramebufferResized = true; +} diff --git a/src/Application/Application.h b/src/Application/Application.h new file mode 100644 index 0000000..a5bc93f --- /dev/null +++ b/src/Application/Application.h @@ -0,0 +1,134 @@ +#ifndef VULKAN_APPLICATION_H +#define VULKAN_APPLICATION_H + +#include +#define GLFW_INCLUDE_VULKAN +#include +#include +#include +#include +#include + +struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; + + bool IsComplete() { + return graphicsFamily.has_value() && presentFamily.has_value(); + } +}; + +struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; +}; + +class Application { + public: + void run(); + + private: + void InitWindow(); + + void InitVulkan(); + void MainLoop(); + void CleanUp(); + + void CreateInstance(); + void SetupDebugMessenger(); + void CreateSurface(); + void PickPhysicalDevice(); + void CreateLogicalDevice(); + void CreateSwapChain(); + void CreateImageViews(); + void CreateRenderPass(); + void CreateGraphicsPipeline(); + void CreateFramebuffers(); + void CreateCommandPool(); + void CreateCommandBuffers(); + + void CleanUpSwapChain(); + + void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex); + + void CreateSyncObjects(); + + void ReCreateSwapChain(); + + void DrawFrame(); + + bool CheckValidationLayerSupport(); + bool CheckDeviceExtensionSupport(VkPhysicalDevice device); + bool IsDeviceSuitable(VkPhysicalDevice device); + std::vector getRequiredExtensions(); + + VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector &availableFormats); + VkPresentModeKHR ChooseSwapPresentMode(const std::vector &availablePresentModes); + VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities); + + QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device); + SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device); + + // key callback + static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); + // FramebufferCallback + static void FramebufferCallback(GLFWwindow *window, int width, int height); + + static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData); + + void PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT &createInfo); + + static std::vector ReadFile(const std::string &filename); + VkShaderModule CreateShaderModule(const std::vector &code); + + private: + GLFWwindow *m_Window = nullptr; + uint32_t m_WIDTH = 800; + uint32_t m_HEIGHT = 600; + + VkInstance m_Instance = VK_NULL_HANDLE; + VkDebugUtilsMessengerEXT m_DebugMessager; + + VkSurfaceKHR m_Surface; + VkSwapchainKHR m_SwapChain; + + std::vector m_SwapChainImages; + VkFormat m_SwapChainImageFormat; + VkExtent2D m_SwapChainExtent; + + std::vector m_SwapChainImageViews; + + VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE; + VkDevice m_Device = VK_NULL_HANDLE; + VkQueue m_GraphicsQueue; + VkQueue m_PresentQueue; + VkRenderPass m_RenderPass; + VkPipelineLayout m_PipelineLayout; + VkPipeline m_GraphicsPipeline; + std::vector m_SwapChainFramebuffers; + VkCommandPool m_CommandPool; + std::vector m_CommandBuffers; + + std::vector m_ImageAvailableSemaphores; + std::vector m_RenderFinishedSemaphores; + std::vector m_InFlightFences; + bool m_FramebufferResized = false; + + const int MAX_FRAMES_IN_FLIGHT = 2; + uint32_t m_CurrrentFrame = 0; + + const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation"}; + + const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + +#ifdef NDEBUG + const bool enableValidationLayer = false; +#else + const bool enableValidationLayer = true; +#endif +}; + +#endif diff --git a/src/Log/Log.cpp b/src/Log/Log.cpp new file mode 100644 index 0000000..2b221a0 --- /dev/null +++ b/src/Log/Log.cpp @@ -0,0 +1,43 @@ +#include "Log.h" + +#include +#include +#include + +namespace Log { + +static bool IsInited = false; + +void Init() { + if (IsInited) { + throw std::runtime_error("spdlog already inited"); + } + IsInited = true; + + // 控制台彩色 sink (多线程安全) + auto console = std::make_shared(); + console->set_pattern("%^[%T.%e] [%l] [%n] %v%$"); + + // 文件 + auto file = std::make_shared("VulkanToturial.log", true); + file->set_pattern("[%Y-%m-%d %T.%e] [%l] [%t] %v"); + + // 多sink 组合默认 logger + + // auto logger = std::make_shared("hello", spdlog::sinks_init_list{console, file}); + auto logger = std::make_shared("Application", spdlog::sinks_init_list{console, file}); + logger->set_level(spdlog::level::trace); + logger->flush_on(spdlog::level::warn); // warn 及以上立即 flush,避免崩溃丢日志 + + spdlog::set_default_logger(logger); +} + +void Shutdown() { + if (!IsInited) { + throw std::runtime_error("spdlog already shutdown or not init"); + } + IsInited = false; + + spdlog::shutdown(); +} +} // namespace Log diff --git a/src/Log/Log.h b/src/Log/Log.h new file mode 100644 index 0000000..19e6f26 --- /dev/null +++ b/src/Log/Log.h @@ -0,0 +1,22 @@ +#ifndef LOG_H +#define LOG_H + +#ifndef SPDLOG_ACTIVE_LEVEL +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE +#endif + +#include + +namespace Log { +void Init(); +void Shutdown(); +}; // namespace Log + +#define LOG_TRACE(...) SPDLOG_TRACE(__VA_ARGS__) +#define LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__) +#define LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__) +#define LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__) +#define LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__) +#define LOG_CRIT(...) SPDLOG_CRITICAL(__VA_ARGS__) + +#endif diff --git a/src/main.cpp b/src/main.cpp index 7e6e478..a999cc8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,8 +2,23 @@ // Created by atdunbg on 2026/8/25. // -#include +#include "Application/Application.h" +#include "Log/Log.h" +#include +#include -int main(int argc, char** argv) { - std::cout << "Hello World!" << std::endl; +int main(int argc, char **argv) { + Log::Init(); + + Application app; + try { + app.run(); + } catch (const std::exception &e) { + LOG_CRIT("{}", e.what()); + Log::Shutdown(); + return EXIT_FAILURE; + } + + Log::Shutdown(); + return EXIT_SUCCESS; }