vulkan初体验之三色三角形

vulkan初体验之三色三角形

文章目录

  • vulkan初体验之三色三角形
    • vulkan与OpenGL
    • 先来个效果图
    • 核心类——VulkanManager类
      • 创建Vulkan实例
      • 销毁Vulkan实例
      • 获取物理设备列表
      • 创建命令缓冲
      • 销毁命令缓冲的方法
      • 初始化交换链
      • 绘制物体

vulkan与OpenGL

都说vulkan是OpenGLnext,相对于OpenGL,vulkan在效率上有着巨量的提升。Vulkan在效率上的提升主要是它天然支持多线程。而且拥有异步数据交换,使用OpenGL时,如果把数据交互放到另一个独立线程中完成,将会引起冲突,这个原因是上传资源和进行绘制时都需要改变上下文,Vulkan则没有这个问题。Vulkan可以并行创建Command Buffer并行绘制,但由于Command Buffer 提交后就都是GPU驱动怎么执行的事了,执行的过程没必要也没可能用多线程加速。 但随之而来的是巨量的代码量的提升,写一个三色三角形需要1500+行的代码,我也不知道我是怎么回事要对这个麻烦的东西产生兴趣,估计不久我就回去继续看OpenGL了…

先来个效果图

这是一个绕x轴旋转的三色三角形
vulkan初体验之三色三角形_第1张图片

接下来着重介绍一下VulkanManager类

核心类——VulkanManager类

该类完成了Vulkan图形应用程序所需的各种基础工作(实例创建、销毁以及绘制)。本质上,该类是Vulkan项目的核心管理者。

#ifndef VULKANEXBASE_MYVULKANMANAGER_H
#define VULKANEXBASE_MYVULKANMANAGER_H

#include 
#include 
#include 
#include "../util/DrawableObjectCommon.h"
#include "ShaderQueueSuit_Common.h"

#define FENCE_TIMEOUT 100000000

class MyVulkanManager
{
public:
	//窗口辅助结构体
	static struct WindowInfo info;
	//vulkan绘制的循环标志
	static bool loopDrawFlag;
	static std::vector<const char *> instanceExtensionNames;//需要使用的实例扩展名称列表
	static VkInstance instance;//Vulkan实例
	static uint32_t gpuCount;//物理设备数量
	static  std::vector<VkPhysicalDevice> gpus;	//物理设备列表
	static uint32_t queueFamilyCount;//物理设备对应的队列家族数量
	static std::vector<VkQueueFamilyProperties> queueFamilyprops;//物理设备对应的队列家族属性列表
	static uint32_t queueGraphicsFamilyIndex;//支持图形工作的队列家族索引
	static VkQueue queueGraphics;//支持图形工作的队列
	static uint32_t queuePresentFamilyIndex;//支持显示工作的队列家族索引
	static std::vector<const char *> deviceExtensionNames;	//所需的设备扩展名称列表
	static VkDevice device;	//逻辑设备
	static VkCommandPool cmdPool;//命令池
	static VkCommandBuffer cmdBuffer;//命令缓冲
	static VkCommandBufferBeginInfo cmd_buf_info;//命令缓冲启动信息
	static VkCommandBuffer cmd_bufs[1];	//供提交执行的命令缓冲数组
	static VkSubmitInfo submit_info[1];	//命令缓冲提交执行信息数组
	static uint32_t screenWidth;//屏幕宽度
	static uint32_t screenHeight;//屏幕高度
	static VkSurfaceKHR surface;//KHR表面
	static std::vector<VkFormat> formats;//KHR表面支持的格式
	static VkSurfaceCapabilitiesKHR surfCapabilities;//表面的能力
	static uint32_t presentModeCount;//显示模式数量
	static std::vector<VkPresentModeKHR> presentModes;//显示模式列表
	static VkExtent2D swapchainExtent;//交换链尺寸
	static VkSwapchainKHR swapChain;//交换链
	static uint32_t swapchainImageCount;//交换链中的图像数量
	static std::vector<VkImage> swapchainImages;//交换链中的图像列表
	static std::vector<VkImageView> swapchainImageViews;//交换链对应的的图像视图列表
	static VkFormat depthFormat;//深度图像格式
	static VkFormatProperties depthFormatProps;	//物理设备支持的深度格式属性
	static VkImage depthImage;//深度缓冲图像
	static VkPhysicalDeviceMemoryProperties memoryroperties;//物理设备内存属性
	static VkDeviceMemory memDepth;	//深度缓冲图像对应的内存
	static VkImageView depthImageView;//深度缓冲图像视图
	static VkSemaphore imageAcquiredSemaphore;//渲染目标图像获取完成信号量
	static uint32_t currentBuffer;//从交换链中获取的当前渲染用图像对应的缓冲编号
	static VkRenderPass renderPass;//渲染通道
	static VkClearValue clear_values[2];//渲染通道用清除帧缓冲深度、颜色附件的数据
	static VkRenderPassBeginInfo rp_begin;//渲染通道启动信息
	static VkFence taskFinishFence;//等待任务完毕的栅栏
	static VkPresentInfoKHR present;//呈现信息
	static VkFramebuffer* framebuffers;//帧缓冲序列首指针
	static ShaderQueueSuit_Common* sqsCL;//着色器管线指针
	static DrawableObjectCommonLight* triForDraw;//绘制用三色三角形物体对象指针
	//三角形旋转角度
	static float xAngle;
	static float yAngle;
	static float zAngle;


	static void init_vulkan_instance();//创建Vulkan实例
	static void enumerate_vulkan_phy_devices();//初始化物理设备
	static void create_vulkan_devices();//创建逻辑设备
	static void create_vulkan_CommandBuffer();//创建命令缓冲
	static void create_vulkan_swapChain();//初始化交换链
	static void create_vulkan_DepthBuffer();//创建深度缓冲相关
	static void create_render_pass();//创建渲染通道
	static void init_queue();//获取设备中支持图形工作的队列
	static void create_frame_buffer();//创建帧缓冲
	static void createDrawableObject();//创建绘制用物体
	static void drawObject();//执行场景中的物体绘制
	static void doVulkan();//启动线程执行Vulkan任务
	static void initPipeline();//初始化管线
	static void createFence();//创建栅栏
	static void initPresentInfo();//初始化显示信息
	static void initMatrix();//初始化矩阵
	static void flushUniformBuffer();//将一致变量数据送入缓冲
	static void flushTexToDesSet();//将纹理等数据与描述集关联
	static void destroyFence();//销毁栅栏
	static void destroyPipeline();//销毁管线
	static void destroyDrawableObject();//销毁绘制用物体
	static void destroy_frame_buffer();//销毁帧缓冲
	static void destroy_render_pass();//销毁渲染通道
	static void destroy_vulkan_DepthBuffer();//销毁深度缓冲相关
	static void destroy_vulkan_swapChain();//销毁交换链
	static void destroy_vulkan_CommandBuffer();//销毁命令缓冲
	static void destroy_vulkan_devices();//销毁逻辑设备
	static void destroy_vulkan_instance();//销毁实例
};


#endif //VULKANEXBASE_MYVULKANMANAGER_H

创建Vulkan实例

相当于OpenGL中 glfwInit()方法的效果。
其中包含了加载Vulkan动态库、初始化所需实例的扩展名称列表、构建应用信息结构体struct等关键步骤。

//创建vulkan实例的方法
void MyVulkanManager::init_vulkan_instance()
{
	
	//初始化所需扩展列表,下列两个扩展是Android下做渲染必须要的
	instanceExtensionNames.push_back(VK_KHR_SURFACE_EXTENSION_NAME);//此处移植Windows不需更改
	instanceExtensionNames.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);//此处移植Windows需要获取VK_KHR_SURFACE_EXTENSION_NAME扩展

    //VkApplicationInfo应用信息
	VkApplicationInfo app_info = {};//构建应用信息结构体实例
	app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;//结构体的类型
	app_info.pNext = NULL;//自定义数据的指针
	app_info.pApplicationName = "HelloVulkan";//应用的名称
	app_info.applicationVersion = 1;//应用的版本号
	app_info.pEngineName = "HelloVulkan";//应用的引擎名称
	app_info.engineVersion = 1;//应用的引擎版本号
	app_info.apiVersion = VK_API_VERSION_1_0;//使用的Vulkan图形应用程序API版本

    //VkInstanceCreateInfo实例创建信息
	VkInstanceCreateInfo inst_info = {};//构建实例创建信息结构体实例
	inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;//结构体的类型
	inst_info.pNext = NULL;//自定义数据的指针
	inst_info.flags = 0;//供将来使用的标志
	inst_info.pApplicationInfo = &app_info;//绑定应用信息结构体
	inst_info.enabledExtensionCount = instanceExtensionNames.size();//扩展的数量
	inst_info.ppEnabledExtensionNames = instanceExtensionNames.data();//扩展名称列表数据
	inst_info.enabledLayerCount = 0;//启动的层数量
	inst_info.ppEnabledLayerNames = NULL;//启动的层名称列表

	VkResult result;//存储运行结果的辅助变量

	//创建Vulkan实例
	result = vkCreateInstance(&inst_info, NULL, &instance);//创建实例
	if (result == VK_SUCCESS)
	{
		printf("Vulkan实例创建成功!\n");
	}
	else
	{
		printf("Vulkan实例创建失败!\n");
	}
}

销毁Vulkan实例

这个比较简单,调用core方法就好

//销毁vulkan实例的方法
void MyVulkanManager::destroy_vulkan_instance()
{
	vkDestroyInstance(instance, NULL);
	printf("Vulkan实例销毁完毕!\n");
	destroy_window(info);
}

获取物理设备列表

这应该是相当于OpenGL里glad初始化方法
这是在创建完实例之后,获取物理设备的数量、内存、list等信息
这里有一个Vulkan编程技巧:先传入空的列表指针,然后在获取后再填充列表

//获取硬件设备的方法(GPU)
void MyVulkanManager::enumerate_vulkan_phy_devices()
{
	gpuCount = 0;//存储物理设备数量的变量
	VkResult result = vkEnumeratePhysicalDevices(instance, &gpuCount, NULL);//获取物理设备数量
	assert(result == VK_SUCCESS);
	printf("[Vulkan硬件设备数量为%d个]", gpuCount);
	gpus.resize(gpuCount);//设置物理设备列表尺寸
	result =vkEnumeratePhysicalDevices(instance, &gpuCount, gpus.data());//填充物理设备列表
	assert(result == VK_SUCCESS);
	vkGetPhysicalDeviceMemoryProperties(gpus[USED_GPU_INDEX], &memoryroperties);//获取第一物理设备的内存属性
}

创建命令缓冲

这里主要包含了构建命令池的各种实例的方法、信息的set以及命令的分配。
主命令缓冲直接提交给队列执行,子命令缓冲通过所属的主命令缓冲来执行,不能直接提交给队列。

//创建命令缓冲的方法
void MyVulkanManager::create_vulkan_CommandBuffer()
{
	VkCommandPoolCreateInfo cmd_pool_info = {};//构建命令池创建信息结构体实例
	cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; 	//给定结构体类型
	cmd_pool_info.pNext = NULL;//自定义数据的指针
	cmd_pool_info.queueFamilyIndex = queueGraphicsFamilyIndex;//绑定所需队列家族索引
	cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;	//执行控制标志
	VkResult result = vkCreateCommandPool(device, &cmd_pool_info, NULL, &cmdPool);//创建命令池
	assert(result == VK_SUCCESS);//检查命令池创建是否成功

	VkCommandBufferAllocateInfo cmdBAI = {};//构建命令缓冲分配信息结构体实例
	cmdBAI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;//给定结构体类型
	cmdBAI.pNext = NULL;//自定义数据的指针
	cmdBAI.commandPool = cmdPool;//指定命令池
	cmdBAI.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;//分配的命令缓冲级别
	cmdBAI.commandBufferCount = 1;//分配的命令缓冲数量
	result = vkAllocateCommandBuffers(device, &cmdBAI, &cmdBuffer);//分配命令缓冲

	assert(result == VK_SUCCESS);//检查分配是否成功
	cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;//给定结构体类型
	cmd_buf_info.pNext = NULL;//自定义数据的指针
	cmd_buf_info.flags = 0;//描述使用标志
	cmd_buf_info.pInheritanceInfo = NULL;//命令缓冲继承信息
	cmd_bufs[0] = cmdBuffer;//要提交到队列执行的命令缓冲数组

	VkPipelineStageFlags* pipe_stage_flags = new VkPipelineStageFlags();//目标管线阶段
	*pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
	submit_info[0].pNext = NULL;//自定义数据的指针
	submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;//给定结构体类型
	submit_info[0].pWaitDstStageMask = pipe_stage_flags;//给定目标管线阶段
	submit_info[0].commandBufferCount = 1;//命令缓冲数量
	submit_info[0].pCommandBuffers = cmd_bufs;//提交的命令缓冲数组
	submit_info[0].signalSemaphoreCount = 0;//信号量数量
	submit_info[0].pSignalSemaphores = NULL;//信号量数组
}

销毁命令缓冲的方法

相对应的我们也要回收命令缓冲,所以需要销毁方法

void MyVulkanManager::destroy_vulkan_CommandBuffer()//销毁命令缓冲的方法
{
	//创建要释放的命令缓冲数组
	VkCommandBuffer cmdBufferArray[1] = { cmdBuffer };
	//释放命令缓冲
	vkFreeCommandBuffers
	(
		device, //所属逻辑设备
		cmdPool,//所属命令池
		1,      //要销毁的命令缓冲数量
		cmdBufferArray//要销毁的命令缓冲数组
	);
	//销毁命令池
	vkDestroyCommandPool(device, cmdPool, NULL);
}

初始化交换链

交换链(SwapChain)是vulkan中执行呈现工作的重要策略,需要呈现模块的vulkan应用程序都应该初始化所需的交换链。

//创建绘制用swapChain的方法
void MyVulkanManager::create_vulkan_swapChain()
{
	//构建KHR表面创建信息结构体实例
	VkWin32SurfaceCreateInfoKHR createInfo;
	createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;//给定结构体类型
	createInfo.pNext = nullptr;//自定义数据的指针
	createInfo.flags = 0;//供未来使用的标志
	createInfo.hwnd = info.window;//给定窗体

	//创建KHR表面
	VkResult result=vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &surface);
	assert(result == VK_SUCCESS);

	//遍历设备对应的队列家族列表
	VkBool32 *pSupportsPresent = (VkBool32 *)malloc(queueFamilyCount * sizeof(VkBool32));
	for (uint32_t i = 0; i < queueFamilyCount; i++)
	{
		vkGetPhysicalDeviceSurfaceSupportKHR(gpus[USED_GPU_INDEX], i, surface, &pSupportsPresent[i]);
		printf("队列家族索引=%d %s显示\n", i, (pSupportsPresent[i] == 1 ? "支持" : "不支持"));
	}

	queueGraphicsFamilyIndex = UINT32_MAX;//支持图形工作的队列家族索引
	queuePresentFamilyIndex = UINT32_MAX;//支持显示(呈现)工作的队列家族索引
	for (uint32_t i = 0; i <queueFamilyCount; ++i)//遍历设备对应的队列家族列表
	{
		//如果当前遍历到的队列家族支持Graphis(图形)工作
		if ((queueFamilyprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)//若此队列家族支持图形工作
		{
			//记录支持Graphis(图形)工作的队列家族索引
			if (queueGraphicsFamilyIndex == UINT32_MAX) queueGraphicsFamilyIndex = i;
			//如果当前遍历到的队列家族支持Present(显示工作)工作
			if (pSupportsPresent[i] == VK_TRUE)//如果当前队列家族支持显示工作
			{
				queueGraphicsFamilyIndex = i;//记录此队列家族索引为支持图形工作的
				queuePresentFamilyIndex = i;//记录此队列家族索引为支持显示工作的
				printf("队列家族索引=%d 同时支持Graphis(图形)和Present(显示)工作\n", i);
				break;
			}
		}
	}

	if (queuePresentFamilyIndex == UINT32_MAX)//若没有找到同时支持两项工作的队列家族
	{
		for (size_t i = 0; i < queueFamilyCount; ++i)//遍历设备对应的队列家族列表
		{
			if (pSupportsPresent[i] == VK_TRUE)//判断是否支持显示工作
			{
				queuePresentFamilyIndex = i;//记录此队列家族索引为支持显示工作的
				break;
			}
		}
	}
	free(pSupportsPresent);//释放存储是否支持呈现工作的布尔值列表

	//没有找到支持Graphis(图形)或Present(显示)工作的队列家族
	//没有找到支持图形或显示工作的队列家族
	if (queueGraphicsFamilyIndex == UINT32_MAX || queuePresentFamilyIndex == UINT32_MAX)
	{
		printf("没有找到支持Graphis(图形)或Present(显示)工作的队列家族\n");
		assert(false);//若没有支持图形或显示操作的队列家族则程序终止
	}

	uint32_t formatCount;//支持的格式数量
	result = vkGetPhysicalDeviceSurfaceFormatsKHR(gpus[0], surface, &formatCount, NULL);//获取支持的格式数量
	printf("支持的格式数量为 %d\n", formatCount);
	VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));//分配对应数量的空间
	formats.resize(formatCount);//调整对应Vector尺寸
	result = vkGetPhysicalDeviceSurfaceFormatsKHR(gpus[0], surface, &formatCount, surfFormats);//获取支持的格式信息
	for (unsigned int i = 0; i<formatCount; i++) {//记录支持的格式信息
		formats[i] = surfFormats[i].format;
		printf("[%d]支持的格式为%d\n", i, formats[i]);
	}
	if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {//特殊情况处理
		formats[0] = VK_FORMAT_B8G8R8A8_UNORM;
	}
	free(surfFormats);

	//获取KHR表面的能力
	result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpus[USED_GPU_INDEX], surface, &surfCapabilities);
	assert(result == VK_SUCCESS);

	//获取支持的显示模式数量
	result = vkGetPhysicalDeviceSurfacePresentModesKHR(gpus[USED_GPU_INDEX], surface, &presentModeCount, NULL);
	assert(result == VK_SUCCESS);
	printf("显示模式数量为%d\n", presentModeCount);

	//调整对应Vector尺寸
	presentModes.resize(presentModeCount);
	//获取支持的显示模式列表
	result = vkGetPhysicalDeviceSurfacePresentModesKHR(gpus[USED_GPU_INDEX], surface, &presentModeCount, presentModes.data());
	for (unsigned int i = 0; i<presentModeCount; i++)
	{
		printf("显示模式[%d]编号为%d\n", i, presentModes[i]);
	}

	VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;//确定交换链显示模式
	for (size_t i = 0; i < presentModeCount; i++)//遍历显示模式列表
	{
		//如果也支持VK_PRESENT_MODE_MAILBOX_KHR模式,由于其效率高,便选用
		if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
		{
			swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
			break;
		}
		if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR))
		{
			//如果没能用上VK_PRESENT_MODE_MAILBOX_KHR模式,但有VK_PRESENT_MODE_IMMEDIATE_KHR模式
			//也比VK_PRESENT_MODE_FIFO_KHR模式强,故选用
			swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
		}
	}

	//确定surface的宽度、高度
	//如果surface能力中的尺寸没有定义(宽度为0xFFFFFFFF表示没定义)
	if (surfCapabilities.currentExtent.width == 0xFFFFFFFF)
	{
		swapchainExtent.width = screenWidth;//设置宽度为窗体宽度
		swapchainExtent.height = screenHeight;//设置高度为窗体高度
		//宽度设置值限制到最大值与最小值之间
		if (swapchainExtent.width < surfCapabilities.minImageExtent.width)
		{
			swapchainExtent.width = surfCapabilities.minImageExtent.width;
		}
		else if (swapchainExtent.width > surfCapabilities.maxImageExtent.width)
		{
			swapchainExtent.width = surfCapabilities.maxImageExtent.width;
		}
		//高度设置值限制到最大值与最小值之间
		if (swapchainExtent.height < surfCapabilities.minImageExtent.height)
		{
			swapchainExtent.height = surfCapabilities.minImageExtent.height;
		}
		else if (swapchainExtent.height > surfCapabilities.maxImageExtent.height)
		{
			swapchainExtent.height = surfCapabilities.maxImageExtent.height;
		}
		printf("使用自己设置的 宽度 %d 高度 %d\n", swapchainExtent.width, swapchainExtent.height);
	}
	else
	{
		//若表面有确定尺寸
		swapchainExtent = surfCapabilities.currentExtent;
		printf("使用获取的surface能力中的 宽度 %d 高度 %d\n", swapchainExtent.width, swapchainExtent.height);
	}

	screenWidth = swapchainExtent.width;//记录实际采用的宽度
	screenHeight = swapchainExtent.height;//记录实际采用的高度

	//期望交换链中的最少图像数量
	uint32_t desiredMinNumberOfSwapChainImages = surfCapabilities.minImageCount + 1;
	//将数量限制到范围内
	if ((surfCapabilities.maxImageCount > 0) && (desiredMinNumberOfSwapChainImages > surfCapabilities.maxImageCount))
	{
		desiredMinNumberOfSwapChainImages = surfCapabilities.maxImageCount;
	}

	//KHR表面变换标志
	VkSurfaceTransformFlagBitsKHR preTransform;
	if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)//若支持所需的变换
	{
		preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
	}
	else//若不支持变换
	{
		preTransform = surfCapabilities.currentTransform;
	}

	VkSwapchainCreateInfoKHR swapchain_ci = {};//构建交换链创建信息结构体实例
	swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;//结构体类型
	swapchain_ci.pNext = NULL;//自定义数据的指针
	swapchain_ci.surface = surface;//指定KHR表面
	swapchain_ci.minImageCount = desiredMinNumberOfSwapChainImages;//最少图像数量
	swapchain_ci.imageFormat = formats[0];//图像格式
	swapchain_ci.imageExtent.width = swapchainExtent.width;//交换链图像宽度
	swapchain_ci.imageExtent.height = swapchainExtent.height;//交换链图像高度
	swapchain_ci.preTransform = preTransform;//指定变换标志
	swapchain_ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;//混合Alpha值
	swapchain_ci.imageArrayLayers = 1;//图像数组层数
	swapchain_ci.presentMode = swapchainPresentMode;//交换链的显示模式
	swapchain_ci.oldSwapchain = VK_NULL_HANDLE;//前导交换链
	swapchain_ci.clipped = true;//开启剪裁
	swapchain_ci.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;//色彩空间
	swapchain_ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;//图像用途
	swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;	//图像共享模式
	swapchain_ci.queueFamilyIndexCount = 0;//队列家族数量
	swapchain_ci.pQueueFamilyIndices = NULL;//队列家族索引列表

	if (queueGraphicsFamilyIndex != queuePresentFamilyIndex)//若支持图形和显示工作的队列家族不相同
	{
		swapchain_ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
		swapchain_ci.queueFamilyIndexCount = 2;//交换链所需的队列家族索引数量为2
		uint32_t queueFamilyIndices[2] = { queueGraphicsFamilyIndex,queuePresentFamilyIndex };
		swapchain_ci.pQueueFamilyIndices = queueFamilyIndices;//交换链所需的队列家族索引列表
	}

	result = vkCreateSwapchainKHR(device, &swapchain_ci, NULL, &swapChain);//创建交换链
	assert(result == VK_SUCCESS);//检查交换链是否创建成功

	 //获取交换链中的图像数量
	result = vkGetSwapchainImagesKHR(device, swapChain, &swapchainImageCount, NULL);
	assert(result == VK_SUCCESS);
	printf("[SwapChain中的Image数量为%d]\n", swapchainImageCount);//检查是否获取成功
	swapchainImages.resize(swapchainImageCount);//调整图像列表尺寸
	//获取交换链中的图像列表
	result =vkGetSwapchainImagesKHR(device, swapChain, &swapchainImageCount, swapchainImages.data());
	assert(result == VK_SUCCESS);
	swapchainImageViews.resize(swapchainImageCount);//调整图像视图列表尺寸
	for (uint32_t i = 0; i < swapchainImageCount; i++)//为交换链中的各幅图像创建图像视图
	{
		VkImageViewCreateInfo color_image_view = {};//构建图像视图创建信息结构体实例
		color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;//设置结构体类型
		color_image_view.pNext = NULL;//自定义数据的指针
		color_image_view.flags = 0;//供将来使用的标志
		color_image_view.image = swapchainImages[i];//对应交换链图像
		color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D;//图像视图的类型
		color_image_view.format = formats[0];//图像视图格式
		color_image_view.components.r = VK_COMPONENT_SWIZZLE_R;//设置R通道调和
		color_image_view.components.g = VK_COMPONENT_SWIZZLE_G;//设置G通道调和
		color_image_view.components.b = VK_COMPONENT_SWIZZLE_B;//设置B通道调和
		color_image_view.components.a = VK_COMPONENT_SWIZZLE_A;//设置A通道调和
		color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;//图像视图使用方面
		color_image_view.subresourceRange.baseMipLevel = 0;//基础Mipmap级别
		color_image_view.subresourceRange.levelCount = 1;//Mipmap级别的数量
		color_image_view.subresourceRange.baseArrayLayer = 0;//基础数组层
		color_image_view.subresourceRange.layerCount = 1;//数组层的数量
		result = vkCreateImageView(device, &color_image_view, NULL, &swapchainImageViews[i]);//创建图像视图
		assert(result == VK_SUCCESS);//检查是否创建成功
	}
}

绘制物体

void MyVulkanManager::drawObject()
{
	FPSUtil::init();//初始化FPS计算
	while (MyVulkanManager::loopDrawFlag)//每循环一次绘制一帧画面
	{
		FPSUtil::calFPS();//计算FPS
		FPSUtil::before();//一帧开始

		//获取交换链中的当前帧索引
		VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, &currentBuffer);
		//为渲染通道设置当前帧缓冲
		rp_begin.framebuffer = framebuffers[currentBuffer];

		vkResetCommandBuffer(cmdBuffer, 0);//恢复命令缓冲到初始状态
		result = vkBeginCommandBuffer(cmdBuffer, &cmd_buf_info);//启动命令缓冲

		MyVulkanManager::flushUniformBuffer();//将当前帧相关数据送入一致变量缓冲
		MyVulkanManager::flushTexToDesSet();//更新绘制用描述集

		vkCmdBeginRenderPass(cmdBuffer, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);//启动渲染通道
		triForDraw->drawSelf(cmdBuffer, sqsCL->pipelineLayout, sqsCL->pipeline, &(sqsCL->descSet[0]));//绘制三色三角形
		vkCmdEndRenderPass(cmdBuffer);//结束渲染通道

		result = vkEndCommandBuffer(cmdBuffer);//结束命令缓冲

		submit_info[0].waitSemaphoreCount = 1;//等待的信号量数量
		submit_info[0].pWaitSemaphores = &imageAcquiredSemaphore;//等待的信号量列表

		result = vkQueueSubmit(queueGraphics, 1, submit_info, taskFinishFence);//提交命令缓冲
		do {	//等待渲染完毕
			result =vkWaitForFences(device, 1, &taskFinishFence, VK_TRUE, FENCE_TIMEOUT);
		} while (result == VK_TIMEOUT);
		vkResetFences(device, 1, &taskFinishFence);//重置栅栏
		
		present.pImageIndices = &currentBuffer;//指定此次呈现的交换链图像索引
		result =vkQueuePresentKHR(queueGraphics, &present);//执行呈现
		FPSUtil::after(60);//限制FPS不超过指定的值
	}
}

你可能感兴趣的:(计算机图形学)