一:概述
本节对Vulkan绘制三角形代码做一个详细的解析,详细情况看代码注释。
二:代码
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>
#include <stdexcept> //std::exception, std::runtime_error
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdlib> //EXIT_SUCCESS, EXIT_FAILURE
#include <cstdint>
#include <limits>
#include <optional> //std::optional
#include <set>
#include <filesystem>
//窗口宽度
const uint32_t WIDTH = 800;
//窗口高度
const uint32_t HEIGHT = 600;
//验证层扩展,启用此层可以帮助开发者诊断错误,验证Vulkan API使用的正确性
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation"
};
//交换链扩展,使用此扩展可以将渲染结果显示在窗口表面上
const std::vector<const char*> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
//如果是Debug模式
#ifdef NDEBUG
//开启校验层
const bool enableValidationLayers = false;
//如果是Release模式
#else
//关闭校验层
const bool enableValidationLayers = true;
#endif
//获取DebugMessage扩展函数指针,并创建VkDebugUtilsMessengerEXT对象
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
//查找 PFN_vkCreateDebugUtilsMessengerEXT 函数实例
auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
//如果找到该函数
if (func != nullptr) {
//创建VkDebugUtilsMessengerEXT对象
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
} else {
//返回扩展不存在
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
//释放VkDebugUtilsMessengerEXT对象
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
//查找 vkDestroyDebugUtilsMessengerEXT 函数实例
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
//如果找到该函数
if (func != nullptr) {
//释放VkDebugUtilsMessengerEXT对象
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;
//可用的显示模式,有立即显示模式,FIFO显示模式等
std::vector<VkPresentModeKHR> presentModes;
};
//渲染三角形的app类
class HelloTriangleApplication {
public:
//运行app
void run() {
//初始化窗口
initWindow();
//初始化Vulkan相关资源
initVulkan();
//渲染循环
mainLoop();
//清理资源
cleanup();
}
private:
//GLFW 窗口
GLFWwindow* window;
//Vulkan 实例
VkInstance instance;
//用来存储回调函数信息
VkDebugUtilsMessengerEXT debugMessenger;
//Vulkan 表面
VkSurfaceKHR surface;
//Vulkan 物理设备
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
//Vulkan 逻辑设备
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;
//命令缓冲区,用来存放命令的
VkCommandBuffer commandBuffer;
//图像呈现信号量
VkSemaphore imageAvailableSemaphore;
//渲染完成信号量
VkSemaphore renderFinishedSemaphore;
//栅栏对象,同于同步两个Command
VkFence inFlightFence;
//初始化窗口
void initWindow() {
//glftInit初始化
glfwInit();
//为Vulkan的设置,告诉GLFW不要创建OpenGL上下文了
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
//禁止调整窗口大小
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
//创建窗口
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
}
//初始化,渲染前准备工作
void initVulkan() {
//创建VkInstance对象
createInstance();
//设置Debug Layer
setupDebugMessenger();
//创建渲染表面
createSurface();
//选择物理设备
pickPhysicalDevice();
//创建逻辑设备
createLogicalDevice();
//创建交换链
createSwapChain();
//创建图像视图
createImageViews();
//创建渲染通道
createRenderPass();
//创建渲染管线
createGraphicsPipeline();
//创建帧缓冲区
createFramebuffers();
//创建命令池
createCommandPool();
//创建命令缓缓冲区
createCommandBuffer();
//创建同步对象
createSyncObjects();
}
//主循环
void mainLoop() {
//如果窗口不关闭,循环
while (!glfwWindowShouldClose(window)) {
//处理窗口事件
glfwPollEvents();
//渲染一帧
drawFrame();
}
//等待设备空闲
vkDeviceWaitIdle(device);
}
//清理释放资源
void cleanup() {
//释放信号量
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);
//释放渲染管线布局对象
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
//释放渲染通道对象
vkDestroyRenderPass(device, renderPass, nullptr);
//循环释放图像视图对象
for (auto imageView : swapChainImageViews) {
//释放图像视图对象
vkDestroyImageView(device, imageView, nullptr);
}
//释放交换链对象
vkDestroySwapchainKHR(device, swapChain, nullptr);
//释放逻辑设备对象
vkDestroyDevice(device, nullptr);
//释放Debug Layer
if (enableValidationLayers) {
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
}
//释放渲染表面对象
vkDestroySurfaceKHR(instance, surface, nullptr);
//释放Vulkan Instance
vkDestroyInstance(instance, nullptr);
//退出窗口
glfwDestroyWindow(window);
//退出glfw
glfwTerminate();
}
//创建Vulkan 实例
void createInstance() {
//如果没开启校验层或者不支持校验层,则抛出一个异常
if (enableValidationLayers && !checkValidationLayerSupport()) {
throw std::runtime_error("validation layers requested, but not available!");
}
//为了创建VkInstance实例,首先要收集下应用程序信息
VkApplicationInfo appInfo{};
//必须是VK_STRUCTURE_TYPE_APPLICATION_INFO,指定结构体类型
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);
//Vulkan版本
appInfo.apiVersion = VK_API_VERSION_1_0;
//VkInstance创建信息
VkInstanceCreateInfo createInfo{};
//必须是VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 指定结构体类型
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
//前面的appInfo
createInfo.pApplicationInfo = &appInfo;
//获取全局(VulkanInstance级别)的扩展信息
auto extensions = getRequiredExtensions();
//扩展的数量
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
//都有哪些扩展
createInfo.ppEnabledExtensionNames = extensions.data();
//创建DebugManager对象,用来调试Vulkan
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
//如果开启校验层
if (enableValidationLayers) {
//配置校验层数量
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
//配置校验层列表
createInfo.ppEnabledLayerNames = validationLayers.data();
//配置DebugMessage信息
populateDebugMessengerCreateInfo(debugCreateInfo);
//在Vulkan中,所有结构的.pNext用来指定扩展信息,此处指向DebugLayer扩展
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
} else {
//否则开启层数为0
createInfo.enabledLayerCount = 0;
//且因为没有扩展信息,置空
createInfo.pNext = nullptr;
}
//创建Vulkan实例
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
//如果创建不成功,抛出异常
throw std::runtime_error("failed to create instance!");
}
}
//配置DebugMessage信息
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
//初始化配置
createInfo = {};
//必须是VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,设置结构体类型
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;
}
//设置DebugMessager信息
void setupDebugMessenger() {
//如果不开启校验层时,就不进行Debug相关配置了
if (!enableValidationLayers) return;
//指定创建DebugMessenger对象的参数
VkDebugUtilsMessengerCreateInfoEXT createInfo;
//设置DebugMessager相关参数
populateDebugMessengerCreateInfo(createInfo);
//创建DebugMessenger对象
if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
//如果创建失败,抛出异常
throw std::runtime_error("failed to set up debug messenger!");
}
}
//创建渲染表面
void createSurface() {
//glfw调用原生窗口平台扩展来创建基于原生窗口的渲染表面
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!");
}
//创建存放VkPhysicalDevice对象的列表
std::vector<VkPhysicalDevice> devices(deviceCount);
//枚举设备,填充VkPhysicalDevice对象信息
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);
//创建一个vector,存放设备队列创建信息
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
//设备队列族(图形 + 显示)
std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()};
//队列优先级,Vulkan允许从0.0 到 1.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<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 {
//否则该字段设置为0
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);
//确定交换链需要多少个图像,通常时会指定交换链运行所需最小数量 + 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 = surface;
//设置交换链运行所需的图像个数
createInfo.minImageCount = imageCount;
//设置像素格式
createInfo.imageFormat = surfaceFormat.format;
//设置颜色空间
createInfo.imageColorSpace = surfaceFormat.colorSpace;
//设置分辨率
createInfo.imageExtent = extent;
//设置每个图像的层数,除非开发立体3D应用,否则该值始终为1
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;
//设置跨队列族数量是2
createInfo.queueFamilyIndexCount = 2;
//设置跨队列族列表
createInfo.pQueueFamilyIndices = queueFamilyIndices;
} else {
//设置图像被一个队列族拥有,在将其用于另一个队列前,必须显示转移所有权
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
}
//设置图像是否支持旋转,如90度旋转
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大小
swapChainImages.resize(imageCount);
//获取交换链中的图像,并存到swapChainImages中
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
//将交换链格式保存到swapChainImageFormat中
swapChainImageFormat = surfaceFormat.format;
//将图像分辨率保存到swapChainExtent中
swapChainExtent = extent;
}
//创建图像视图
void createImageViews() {
//调整swapChainImageViews大小,和swapChainImages一致
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];
//设置图像类别 2D
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;
//设置没有mipmapping
createInfo.subresourceRange.baseMipLevel = 0;
//设置图像 mipmapping 级别为 1
createInfo.subresourceRange.levelCount = 1;
//设置单层,无多层图像
createInfo.subresourceRange.baseArrayLayer = 0;
//设置图像层数为 1
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;
//设置像素采样个数 1
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
//loadOp定义了在显示前如何处理附件中的图像数据,本例是清除
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
//storeOp定义了在显示后如何处理附件中的图像数据,本例是保留现有附件中的内容
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;
//指定渲染结束后,图像数据的布局,如果希望在渲染后能在交换链中显示,就设置为 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
//每个渲染通道引用一个或多个附件,这里设置附件引用信息
VkAttachmentReference colorAttachmentRef{};
//0 代表第一个附件,及上面设置的附件
colorAttachmentRef.attachment = 0;
//在这里,我们打算将该附件作为颜色缓冲区使用
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
//Vulkan中一个渲染通道由一个或多个子通道组成,每一个子通道使用前一个通道中帧缓冲区的内容,本例中只有一个渲染子通道
//设置渲染子通道信息
VkSubpassDescription subpass{};
//Vulkan将来要支持计算子通道,本例这里需显示指定为图像子通道
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
//颜色附件个数为 1
subpass.colorAttachmentCount = 1;
//设置颜色附件引用
subpass.pColorAttachments = &colorAttachmentRef;
//设置子通道间的依赖关系
VkSubpassDependency dependency{};
//本例只有一个子通道,因为是第一个子通道,它对前面没有任何依赖,所以设置为VK_SUBPASS_EXTERNAL
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
//下一个子通道要依赖当前子通道,所以索引设置为 0
dependency.dstSubpass = 0;
//指定等待的操作(无),因为是第一个子通道,那就不同等待任何操作
dependency.srcStageMask = VK_PIPELINE_STAGE_NONE_KHR;
//指定等待发生的阶段(无),因为是第一个子通道,那就是不用等
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 path = std::filesystem::current_path();
std::cout << "path" << path << std::endl;
//读取顶点着色器代码
auto vertShaderCode = readFile(path.string() + "/shaders/vert.spv");
//读取片段着色器代码
auto fragShaderCode = readFile(path.string() + "/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;
//设置顶点数据之间的间隔
vertexInputInfo.vertexBindingDescriptionCount = 0;
//传递给顶点着色器顶点属性数据个数,本例为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;
//设置viewport信息
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
//设置viewport 个数
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;
//线宽1.0
rasterizer.lineWidth = 1.0f;
//背面裁剪
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
//逆时针为正面
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
//设置depthBias, 此处不设置,这个一般用来解决两个三角形离得非常近而产生抖动的问题,
rasterizer.depthBiasEnable = VK_FALSE;
//设置多重采样
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
//不开启多重采样
multisampling.sampleShadingEnable = VK_FALSE;
//采样像素点默认为 1
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;
//是否开启按位混合,此处为False
colorBlending.logicOpEnable = VK_FALSE;
//按位的操作
colorBlending.logicOp = VK_LOGIC_OP_COPY;
//附件个数
colorBlending.attachmentCount = 1;
//附件混合信息
colorBlending.pAttachments = &colorBlendAttachment;
//设置rgba的混合常数
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
//管线中也允许少量动态更改状态信息,比如动态调整视口,裁剪窗口信息
std::vector<VkDynamicState> 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<uint32_t>(dynamicStates.size());
//动态状态信息
dynamicState.pDynamicStates = dynamicStates.data();
//配置管线布局信息,用来设置给着色器传递数据,比如uniform等,本例不设置这些,只初始化一个空的结构体变量
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
//布局数量为 0
pipelineLayoutInfo.setLayoutCount = 0;
//布局常量个数为 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;
//目前只有一个管线,设置为VK_NULL_HANDLE
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() {
//设置帧缓冲区个数同swapChainImageViews个数
swapChainFramebuffers.resize(swapChainImageViews.size());
//遍历 swapChainImageViews
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;
//设置附件个数,1个图像视图
framebufferInfo.attachmentCount = 1;
//设置附件视图
framebufferInfo.pAttachments = attachments;
//设置帧缓冲的宽
framebufferInfo.width = swapChainExtent.width;
//设置帧缓冲的高
framebufferInfo.height = swapChainExtent.height;
//设置帧缓冲的层数 1, 是2D帧缓冲区
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.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() {
//配置命令缓冲区
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
//配置命令池,缓冲区从该池中分配
allocInfo.commandPool = commandPool;
//主缓冲区,可以提到的命令队列中执行
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
//命令缓冲区个数为 1,本例我们只分配了一个命令缓冲区
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;
//总是通过 vkBeginCommandBuffer 来开始记录命令缓冲区
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
throw std::runtime_error("failed to begin recording command buffer!");
}
//为vkCmdBeginRenderPass配置一些参数信息,绘图马上开始
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
//配置渲染通道
renderPassInfo.renderPass = renderPass;
//配置framebuffer
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
//配置绘制区域
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = swapChainExtent;
//配置清屏信息
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
//clear个数
renderPassInfo.clearValueCount = 1;
//clear颜色
renderPassInfo.pClearValues = &clearColor;
//开始绘图,渲染命令将会记录到commandBuffer中
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
//绑定渲染管线,这管线里面记录了渲染状态
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
//配置动态渲染状态,视口信息
VkViewport viewport{};
//x 位置
viewport.x = 0.0f;
//y 位置
viewport.y = 0.0f;
//width 值
viewport.width = static_cast<float>(swapChainExtent.width);
//height 值
viewport.height = static_cast<float>(swapChainExtent.height);
//minDepth 值
viewport.minDepth = 0.0f;
//maxDepth 值
viewport.maxDepth = 1.0f;
//配置视口的命令
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
//配置动态渲染状态,裁剪窗口信息
VkRect2D scissor{};
//offset 值
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;
//创建一个信号量 imageAvailableSemaphore,用来表示图像已经从交换链中获取并准备好进行渲染
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS ||
//创建一个信号量 renderFinishedSemaphore,用来表示渲染已经完成并可以进行显示了
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS ||
//创建一个栅栏 inFlightFence,用来确保一次只渲染一帧
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence) != VK_SUCCESS) {
throw std::runtime_error("failed to create synchronization objects for a frame!");
}
}
//绘制一帧
void drawFrame() {
//在一帧的开始,我们要等待上一帧完成,以便命令缓冲区可用,为此调用vkWaitForFences等待,注意在第一帧时不会等,由于inFlightFence一开始是有信号的
vkWaitForFences(device, 1, &inFlightFence, VK_TRUE, UINT64_MAX);
//等到之后,手动将 inFlightFence 置为无信号状态
vkResetFences(device, 1, &inFlightFence);
//交换链中的当前可用的图像索引
uint32_t imageIndex;
//查询交换链中当前可用的图像索引,交换链显示完图像后会将imageAvailableSemaphore置为有信号
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
//重置命令缓冲区
vkResetCommandBuffer(commandBuffer, /*VkCommandBufferResetFlagBits*/ 0);
//重新在缓冲区中记录命令, 使用imageIndex的图像
recordCommandBuffer(commandBuffer, imageIndex);
//记录完命令之后,现在要提交命令到硬件上执行了,先配置下提交命令的信息
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
//waitSemaphores 指定了在执行命令之前要等待哪个信号量, 这里是要等待图像已经从交换链中获取
VkSemaphore waitSemaphores[] = {imageAvailableSemaphore};
//waitStages 指定了在管道的哪个阶段等待,这里希望是完成颜色缓冲区中的数据写入,因此是颜色附件的输出阶段
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
//指定等待信号量个数是 1
submitInfo.waitSemaphoreCount = 1;
//指定等待的信号量
submitInfo.pWaitSemaphores = waitSemaphores;
//指定等待的阶段
submitInfo.pWaitDstStageMask = waitStages;
//设置命令缓冲区个数
submitInfo.commandBufferCount = 1;
//绑定命令缓冲区
submitInfo.pCommandBuffers = &commandBuffer;
//设置在命令缓冲区中的命令执行完成之后激活哪个信号量,本例是renderFinishedSemaphore
VkSemaphore signalSemaphores[] = {renderFinishedSemaphore};
//设置激活的信号量个数
submitInfo.signalSemaphoreCount = 1;
//设置激活的信号量
submitInfo.pSignalSemaphores = signalSemaphores;
//将命令缓冲区的命令提交到图形队列,当命令缓冲区命令执行完成时,inFlightFence将置为有信号,这样就和开头的同步接上了
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;
//等待信号量的个数是 1
presentInfo.waitSemaphoreCount = 1;
//因为我们要等待命令缓冲区命令执行完成,所以这里等待的是 signalSemaphores, 即 renderFinishedSemaphore
presentInfo.pWaitSemaphores = signalSemaphores;
//指定显示结果的交换链,这里就是 swapChain
VkSwapchainKHR swapChains[] = {swapChain};
//指定交换链的个数
presentInfo.swapchainCount = 1;
//指定交换链
presentInfo.pSwapchains = swapChains;
//指定显示图像的索引
presentInfo.pImageIndices = &imageIndex;
//提交显示请求,将图像显示给交换链
vkQueuePresentKHR(presentQueue, &presentInfo);
}
//创建着色器模块
VkShaderModule createShaderModule(const std::vector<char>& code) {
//配置着色器信息
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
//着色器代码size
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) {
//选取RGBA像素格式和SRGB颜色空间
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) {
//选取MAILBOX模式,这种模式会等待vsync时更新图像,不会导致画面撕裂
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
return availablePresentMode;
}
}
//如果没有匹配到,则返回FIFO模式
return VK_PRESENT_MODE_FIFO_KHR;
}
//选择一个屏幕分辨率
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
//如果capabilities.currentExtent.width有值
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
//则返回capabilities.currentExtent的分辨率
return capabilities.currentExtent;
} else {
//否则获取窗口大小
int width, height;
glfwGetFramebufferSize(window, &width, &height);
//赋值给 actualExtent
VkExtent2D actualExtent = {
static_cast<uint32_t>(width),
static_cast<uint32_t>(height)
};
//确保actualExtent当前值不超过capabilities所支持的范围
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);
//如果支持格式个数大于0
if (formatCount != 0) {
//存到details结构体变量中
details.formats.resize(formatCount);
//将格式信息存到details结构体变量中
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
}
//可用的显示模式个数
uint32_t presentModeCount;
//查询可用的显示模式个数
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
//如果有可用的显示模式
if (presentModeCount != 0) {
//存到details中
details.presentModes.resize(presentModeCount);
//查询显示模式并存到details中
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());
//将请求支持的扩展转存到set集合中
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
//遍历当前设备所支持的扩展
for (const auto& extension : availableExtensions) {
//是否在所请求的扩展列表中
requiredExtensions.erase(extension.extensionName);
}
//如果所请求的扩展都被设备所支持,则返回true,否则返回false
return requiredExtensions.empty();
}
//检查设备支持哪些队列族
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
//存放队列族信息
QueueFamilyIndices indices;
//队列族数量
uint32_t queueFamilyCount = 0;
//获取设备支持的队列族数量
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
//创建vector来存放队列族对象
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) {
//记住id
indices.graphicsFamily = i;
}
VkBool32 presentSupport = false;
//是否具有显示功能
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
//如果支持
if (presentSupport) {
//记住id
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);
//将扩展列表存到vector中
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
//如果开启了校验层,则加入VK_EXT_DEBUG_UTILS_EXTENSION_NAME扩展
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;
}
//设置Debug回调函数
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {
//向标准错误流中输出回调消息
std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
//按Vulkan的规定,应用层总是返回VK_FALSE
return VK_FALSE;
}
};
int main() {
//创建app
HelloTriangleApplication app;
try {
//允许
app.run();
} catch (const std::exception& e) {
//捕捉异常
std::cerr << e.what() << std::endl;
//返回错误
return EXIT_FAILURE;
}
//正常退出
return EXIT_SUCCESS;
}