【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形

在前一篇中完成了对Vulkan的初始化和三角形的绘制,其中很多东西还没有被用到,这一节最终将绘制这样两个重叠的矩形,并且它们会一直绕着屏幕中心点进行旋转。
【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形_第1张图片
将要补充使用的内容有:VertexBuffer、IndexBuffer、StagingBuffer、UniformBuffer的创建和使用,深度缓冲的创建和设定。
代码将基于第一节的内容进行增添和修改。

1. 创建VertexBuffer、IndexBuffer、StagingBuffer

在前一节绘制三角形时,采用的是在Shader中对三角形的屏幕坐标进行硬编码的形式来绘制,而一般的渲染应该是通过输入顶点,让顶点经过一系列的变换来最终获得它们在屏幕上的位置,所以我们需要创建VertexBuffer和IndexBuffer来指定顶点的数据。

首先先修改一下VertexShader的代码:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec3 inColor;

layout(location = 0) out vec3 fragColor;

void main() {
    gl_Position = vec4(inPosition, 0.0, 1.0);
    fragColor = inColor;
}

顶点的输入数据有inPosition和inColor,注意这里的Location,它们将会与之后创建描述符进行绑定时对应。

然后我们需要提供这样的结构体作为顶点输入:

struct Vertex 
{
	float pos[3];
	float color[3];
}

然后创建这样一组顶点数据:

const std::vector vertices = {
	{{-0.2f, -0.2f , 0.5f}, {0.0f, 1.0f, 0.0f}},
	{{0.8f, -0.2f , 0.5f}, {0.0f, 1.0f, 0.0f}},
	{{0.8f, 0.8f , 0.5f}, {0.0f, 1.0f, 0.0f}},
	{{-0.2f, 0.8f , 0.5f}, {0.0f, 1.0f, 0.0f}},

	{{-0.5f, -0.5f , 0.4f}, {1.0f, 0.0f, 0.0f}},
	{{0.5f, -0.5f , 0.4f}, {1.0f, 0.0f, 0.0f}},
	{{0.5f, 0.5f , 0.4f}, {1.0f, 0.0f, 0.0f}},
	{{-0.5f, 0.5f , 0.4f}, {1.0f, 0.0f, 0.0f}}
};

和索引数据:

const std::vector indices = {
	0, 2, 1, 3, 2, 0,
	4, 6, 5, 6, 4, 7
};

现在的目标是将这些数据配置到pipeline上,所以要先创建VertexBuffer和IndexBuffer,在Vulkan中,创建各种不同的Buffer在形式上都是差不多的,只是在参数细节上有所不同,于是添加一个创建Buffer的函数:

void CreateBuffer(
	VkDeviceSize size,
	VkBufferUsageFlags usage,
	VkMemoryPropertyFlags properties,
	VkBuffer& buffer,
	VkDeviceMemory& memory
) {
	VkBufferCreateInfo bufferInfo = {};
	bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
	bufferInfo.size = size;
	bufferInfo.usage = usage;
	bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

	if (vkCreateBuffer(logicalDevice, &bufferInfo, NULL, &buffer) != VK_SUCCESS)
	{
		throw std::runtime_error(" create buffer fault . ");
	}

	VkMemoryRequirements memory_requirements;
	vkGetBufferMemoryRequirements(logicalDevice, buffer, &memory_requirements);

	VkPhysicalDeviceMemoryProperties memProperties;
	vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
	
	int memInd = -1;
	for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
	{
		if ( ( memory_requirements.memoryTypeBits & ( 1 << i ) ) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
		{
			memInd = i;
			break;
		}
	}
	if (memInd == -1)
	{
		throw std::runtime_error("no suitable memory for vertex buffer . ");
	}

	VkMemoryAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
	allocInfo.allocationSize = memory_requirements.size;
	allocInfo.memoryTypeIndex = memInd;

	if (vkAllocateMemory(logicalDevice, &allocInfo, NULL, &memory) != VK_SUCCESS)
	{
		throw std::runtime_error(" allocate memory fault . ");
	}

	vkBindBufferMemory(logicalDevice, buffer, memory, 0);
}

首先要明确一点,在Vulkan中,所有的Buffer或者Image在创建时,都不会分配相应的内存,这点和Direct3D完全不同,在D3D中我们只需要创建一个Buffer,它的内存自动的就被分配好了,而在Vulkan中,必须要手动的去分配与之对应的内存,然后将两者绑定起来(最后一行),因此这个函数要处理一些与内存分配有关的任务。

关于参数:

参数 含义
VkDeviceSize size 所需要创建的Buffer所占内存的大小
VkBufferUsageFlags usage 所要创建的Buffer的用途
VkMemoryPropertyFlags properties 为Buffer分配的内存的特性
VkBuffer& buffer 创建的buffer
VkDeviceMemory& memory 所要分配的内存

其中VkBufferUsageFlags和VkMemoryPropertyFlags比较麻烦,在官方文档中,给出了这两个东西的具体取值:
【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形_第2张图片

【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形_第3张图片
在本程序中只会用到图中标红线的部分。
VkBufferUsageFlags中标红线的一类是Buffer的类型,比如VertexBuffer、IndexBuffer之类的;还有一类跟Transfer有关,表明这些BUFFER中的数据可能会被拷贝到其他的Buffer或者从其他的Buffer拷贝过来。
VkMemoryPropertyFlagBits中标红线的:
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT:使用这个标记的内存能够最快地被GPU访问,但是不能被CPU访问,它不能被映射到CPU上。
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT:使用这个标记的内存可以被映射到CPU(HOST)上,这意味着不带有这个标记的内存,是不能直接被CPU访问的,它必须要先间接地转移到其他可被映射到CPU的内存中,才能再被CPU访问。
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT:使用这个标记的内存在被映射到CPU上后,再CPU上对它进行的操作,不会被Cache,对它做的一切修改,不需要手动地写回

还有一个对应的VK_MEMORY_PROPERTY_CHCHED_BIT,它表示在CPU上所做的一切修改都会先被Cache下来,必须要手动地将修改写回(调用vkFlushMappedMemoryRanges和 vkInvalidateMappedMemoryRanges)才能让GPU得到修改后的内存数据。

然后函数的定义分为两部分,一部分是创建Buffer,这一部分比较简单,填充好CreateInfo,然后进行创建即可。
第二部分是分配内存,它涉及到三个数据类型:VkMemoryRequirements-要为Buffer分配的内存的要求、VkPhysicalDeviceMemoryProperties-GPU所支持分配的各种内存的性质、VkMemoryAllocateInfo-具体的内存分配信息

typedef struct VkMemoryRequirements {
    VkDeviceSize    size;
    VkDeviceSize    alignment;
    uint32_t        memoryTypeBits;
} VkMemoryRequirements;

VkMemoryRequirements中,size表示分配的内存的容量大小,alignment表示内存的偏移,memoryTypeBits是一个掩码,对应的某一位为1表示它支持相应位所代表的的内存类型。

typedef struct VkPhysicalDeviceMemoryProperties {
    uint32_t        memoryTypeCount;
    VkMemoryType    memoryTypes[VK_MAX_MEMORY_TYPES];
    uint32_t        memoryHeapCount;
    VkMemoryHeap    memoryHeaps[VK_MAX_MEMORY_HEAPS];
} VkPhysicalDeviceMemoryProperties;

VkPhysicalDeviceMemoryProperties中记录了GPU所支持的所有内存类型和内存Heap。每一块被分配的内存都来自于一个Heap,VkMemoryType中记录了内存来自的Heap和此前提到的VkMemoryPropertyFlagBits。

通过语句

if ( ( memory_requirements.memoryTypeBits & ( 1 << i ) ) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)

来筛选符合VkMemoryRequirements所要求的以及满足VkMemoryPropertyFlagBits properties的内存类型编号,将其传入VkMemoryAllocateInfo,随后执行内存分配。

现在有了这样一个创建Buffer的函数,要怎样选择参数来创建需要的VertexBuffer和IndexBuffer呢?
考虑到VertexBuffer和IndexBuffer在管线中频繁地被GPU读取,而不需要被CPU进行读写,因此让GPU读取的越快越好,于是应该让它们的 VkMemoryPropertyFlagBits设为VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
但是这样就没办法将顶点和索引的数据直接放到它们的内存上,因为带有这个标记的内存是不可以被映射到CPU上的。

为了解决这个问题,引入StagingBuffer,这个Buffer是专门用于做CPU传到GPU的一个中介,我们可以先将这个Buffer映射到CPU上,然后将数据传递到它的内存中,再利用一个内存转移命令,将StagingBuffer中的数据转移到VertexBuffer和IndexBuffer中,这样虽然在初始化数据的时候麻烦了一点,但是保证了GPU读取它们的高效性。

因此需要给VertexBuffer和IndexBuffer的VkBufferUsageFlags添加一个VK_BUFFER_USAGE_TRANSFER_DST_BIT,让它们作为内存转移指令的目标Buffer。

于是得到创建这三个Buffer的程序:

	VkDeviceSize vertSize = sizeof(Vertex) * vertices.size();
	VkDeviceSize indexSize = sizeof(indices[0]) * indices.size();

	VkBuffer vertexBuffer;
	VkDeviceMemory vertexBufferMemory;
	CreateBuffer(vertSize,
		VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
		vertexBuffer,
		vertexBufferMemory
	);

	VkBuffer indexBuffer;
	VkDeviceMemory indexBufferMemory;
	CreateBuffer(indexSize,
		VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
		indexBuffer,
		indexBufferMemory);

	VkBuffer stagingBuffer;
	VkDeviceMemory stagingBufferMemory;
	CreateBuffer(vertSize + indexSize,
		VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
		VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
		stagingBuffer,
		stagingBufferMemory);

然后将Vertex和Index的数据先映射到StagingBuffer中:

	void* data;
	vkMapMemory(logicalDevice , stagingBufferMemory , 0 , vertSize , 0 , &data );
	memcpy(data, vertices.data(), vertSize);
	memcpy((char*)data + vertSize, indices.data(), indexSize);
	vkUnmapMemory(logicalDevice, stagingBufferMemory);

接下里要进行内存转移,这一部分比较麻烦,内存转移也是一个Command,我们需要用到第一节中用过的CommandBuffer:

void CopyBuffer(
	VkBuffer srcBuffer,
	VkBuffer dstBuffer,
	VkDeviceSize size,
	VkDeviceSize srcOffset 
) {
	VkCommandBufferAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
	allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
	allocInfo.commandPool = commandPool;
	allocInfo.commandBufferCount = 1;

	VkCommandBuffer commandBuffer;
	vkAllocateCommandBuffers(logicalDevice, &allocInfo, &commandBuffer);

	VkCommandBufferBeginInfo beginInfo = {};
	beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
	beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

	vkBeginCommandBuffer(commandBuffer, &beginInfo);
	
	VkBufferCopy copyRegion = {};
	copyRegion.dstOffset = 0;
	copyRegion.srcOffset = srcOffset;
	copyRegion.size = size;
	vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);

	vkEndCommandBuffer(commandBuffer);

	VkSubmitInfo submitInfo = {};
	submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
	submitInfo.commandBufferCount = 1;
	submitInfo.pCommandBuffers = &commandBuffer;

	vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE );
	vkQueueWaitIdle(graphicsQueue);

	vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer);
}

注意这里设置了beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;,因为这个CommandBuffer只会在程序开始时被执行一次。
命令的主体是:

	VkBufferCopy copyRegion = {};
	copyRegion.dstOffset = 0;
	copyRegion.srcOffset = srcOffset;
	copyRegion.size = size;
	vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);

VkBufferCopy指定了拷贝时源数据和目标数据在内存中的偏移以及数据的大小,注意到stagingBuffer中的数据是VertexData+IndexData,因此在传递IndexData时,需要注意指定srcOffset

然后调用:

	CopyBuffer(stagingBuffer, vertexBuffer, vertSize , 0 );
	CopyBuffer(stagingBuffer, indexBuffer, indexSize, vertSize);

来完成内存转移。

这样VertexBuffer和IndexBuffer的数据就已经设置完成,现在需要将它们绑定到Pipeline上。
在渲染用的CommandBuffer中,加入:

		VkBuffer vertex_buffers[] = { vertexBuffer };
		VkDeviceSize offsets[] = { 0 };
		vkCmdBindVertexBuffers(commandBuffer[i], 0, 1, vertex_buffers, offsets);
		vkCmdBindIndexBuffer(commandBuffer[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);

2.配置VertexInputInfo

上一节的7.7.2.4创建VertexInputInfo时,并未配置有意义的信息,这里需要对其做修改,目前我们只是将顶点的数据绑定到了Pipeline中,但是没有将其与VertexShader的输入顶点数据做对应。将Vertex结构体修改为:

struct Vertex {
	float pos[3];
	float color[3];

	static VkVertexInputBindingDescription getBindingDescription() {
		VkVertexInputBindingDescription bindingDescription = {};
		bindingDescription.binding = 0;
		bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
		bindingDescription.stride = sizeof(Vertex);
		return bindingDescription;
	}

	static std::array getAttributeDescription() {
		std::array attributeDescriptions = {};
		attributeDescriptions[0].binding = 0;
		attributeDescriptions[0].location = 0;
		attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[0].offset = offsetof(Vertex , pos);

		attributeDescriptions[1].binding = 0;
		attributeDescriptions[1].location = 1;
		attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[1].offset = offsetof(Vertex, color);
		return attributeDescriptions;
	}
};

加入了两个静态函数,它们分别获取BindingDescription和AttributeDescription
Vertex中的每一个字段对应一个Attribute,一个binding中包含多个Attribute,每一个Binding有InputRate(逐顶点或逐Instance)和stride(包含的所有Attribute的大小),每个Attribute指定它们的绑定号、location(与VertexShader中的Location对应)、格式和偏移量。

然后修改7.7.2.4:

	//7.7.2.4 create vertex input info 
	auto bindingDescription = Vertex::getBindingDescription();
	auto attributeDescription = Vertex::getAttributeDescription();
	VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
	vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
	vertexInputInfo.vertexBindingDescriptionCount = 1;
	vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
	vertexInputInfo.vertexAttributeDescriptionCount = attributeDescription.size();
	vertexInputInfo.pVertexAttributeDescriptions = attributeDescription.data();

这段程序比较好理解,不多做说明。

到这里就完成了顶点配置所需要的全部工作。

3.创建Uniform Buffer

现在有了顶点在模型空间下的数据,我们需要对其做World、View、Proj三个变换得到它们在裁剪空间下的坐标,于是需要配置存储变换矩阵的Buffer,在Direct3D中则为ConstantBuffer,在Vulkan中,由UniformBuffer来完成这项工作。
先修改VertexShader:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(binding = 0) uniform UniformBufferObject {
    mat4 model;
    mat4 view;
    mat4 proj;
} ubo;

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;

layout(location = 0) out vec3 fragColor;

void main() {
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
}

然后在程序中准备好UniformBuffer的数据:

struct UniformBufferObject
{
	glm::mat4 model;
	glm::mat4 view;
	glm::mat4 proj;
}ubo;

注意Vulkan本身不带数学库,这里使用了GLM来作为数学库。

下面我们要来创建UniformBuffer,不过先考虑一共需要几个UniformBuffer,直观的想法是一个就够了,因为每一帧渲染都只会用到一次这个Buffer。但是需要注意到,如果当渲染某一帧时GPU没有渲染完,这个时候CPU会先进入下一帧进行运算,如果CPU在下一帧时对这个Buffer做了修改,使它与前一帧不一样,那么此时GPU后续的渲染就会出现问题,至少它会出现某种不一致的现象。所以应该对每个Swapchain Image都分别创建一个UniformBuffer,每一个CommandBuffer中要绑定不同的UniformBuffer。

	std::vector uniformBuffers;
	std::vector uniformBuffersMemory;
	VkDeviceSize bufferSize = sizeof(UniformBufferObject);
	uniformBuffers.resize(swapChainImages.size());
	uniformBuffersMemory.resize(swapChainImages.size());

	for (size_t i = 0; i < swapChainImages.size(); i++)
	{
		CreateBuffer(bufferSize,
			VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
			VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
			uniformBuffers[i],
			uniformBuffersMemory[i]
		);
	}

对于UniformBuffer中的每一个矩阵都需要CPU在每一帧进行更新,于是我们在主循环中加入:

		static auto startTime = std::chrono::high_resolution_clock::now();
		auto currentTime = std::chrono::high_resolution_clock::now();
		float time = std::chrono::duration(currentTime - startTime).count();
		UniformBufferObject ubo = {};
		ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
		ubo.view = glm::lookAt(glm::vec3(0.0f, 0.0f, -5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
		ubo.proj = glm::perspective(glm::radians(45.0f), actualExtent.width / (float)actualExtent.height, 0.1f, 10.0f);
		ubo.proj[1][1] *= -1;
		void* data;
		vkMapMemory(logicalDevice, uniformBuffersMemory[imageIndex], 0, sizeof(ubo), 0, &data);
		memcpy(data, &ubo, sizeof(ubo));
		vkUnmapMemory(logicalDevice, uniformBuffersMemory[imageIndex]);

注意我们将透视投影矩阵的第2行第2列的元素取了相反数,因为Vulkan中关于Y轴从上(-1)往下(1)是正方向,而裁剪空间的Y轴是从下(-1)往上(1)为正方向,因此我们需要将执行运算的Y坐标全部反过来,也就是要让投影矩阵的第二列元素全部乘-1,而投影矩阵的第二列中只有第二行是非零数,所以只用乘一个就行。

还是同样的问题,现在UniformBuffer已经有了数据,但是它还是没有和VertexShader中的声明相对应。
要完成这个任务,需要对7.7.9进行修改,不过在此之前先区分几个概念:

Vulkan中最基本的绑定单元是Descriptor,一个Descriptor可以将一个资源绑定到管线上。

VkDescriptorSetLayoutBindingVkDescriptorSetLayoutVkDescriptorPoolVkDescriptorSetVkWriteDescriptorSet

VkDescriptorSetLayoutBinding:内含多个Descriptor,一个Descriptor对应一个UniformBuffer,每个VkDescriptorSetLayoutBinding都自己的binding号,这个binding与VertexShader中声明的Binding相对应。也就是说一个Binding中包含一个UniformBuffer数组。每个Binding都会指定绑定到的管线阶段(VertexShader、FragmentShader等)

VkDescriptorSet:包含多个binding,并且为binding中的所有Descriptor分配了内存。直接被CommandBuffer所使用的结构,如果需要改变绑定到某个Shader中的资源,就需要在CommandBuffer中绑定不同的VkDescriptorSet

VkDescriptorSetLayout:DescriptorSet的布局,声明一个DescriptorSet中的所有Binding,但是并没有生成实际的DescriptorSet,只代表DescriptorSet的结构,它被传入pipelineLayoutInfo和VkDescriptorPool中

VkDescriptorPool:用于生成DescriptorSet

VkWriteDescriptorSet:用于将具体的UniformBuffer和DescriptorSet中的某一个Binding下的某一个Descriptor绑定。

VertexShader中在声明UniformBuffer时,可以这样写:

layout(set = 0, binding = 0) uniform UniformBufferObject { ... }

set代表的就是VkDescriptorSet,Binding就是在相应的Set下的Binding编号。

下面这段程序先创建一个Binding,将所有的Descriptor(这里只有一个)绑定到VertexShader上,然后指定Descriptor所代表的类型是UniformBuffer。
然后创建一个VkDescriptorSetLayout,它包含了所有的binding。
然后将它传入pipelineLayout和DescriptorPool,指定DescriptorPool将要创建的descriptor个数,然后用它来创建DescriptorSet。
最后通过vkUpdateDescriptorSets将UniformBuffer真正地与Descriptor对应起来,经由Descriptor绑定到VertexShader中。

	//7.7.9 create pipeline layout 
	VkDescriptorSetLayoutBinding uboLayoutBinding = {};
	uboLayoutBinding.binding = 0;
	uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
	uboLayoutBinding.descriptorCount = 1;
	uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
	uboLayoutBinding.pImmutableSamplers = NULL;

	VkDescriptorSetLayout descriptorSetLayout;
	VkDescriptorSetLayoutCreateInfo layoutInfo = {};
	layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
	layoutInfo.bindingCount = 1;
	layoutInfo.pBindings = &uboLayoutBinding;

	if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, NULL, &descriptorSetLayout) != VK_SUCCESS)
	{
		throw std::runtime_error(" create descriptor set layout fault . ");
	}

	VkPipelineLayout pipelineLayout;
	VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
	pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
	pipelineLayoutInfo.setLayoutCount = 1;
	pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;

	if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, NULL, &pipelineLayout) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create graphics pipeline . ");
	}

	VkDescriptorPoolSize poolSize = {};
	poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
	poolSize.descriptorCount = static_cast(swapChainImages.size());
	
	VkDescriptorPoolCreateInfo poolInfo = {};
	poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
	poolInfo.poolSizeCount = 1;
	poolInfo.pPoolSizes = &poolSize;
	poolInfo.maxSets = static_cast(swapChainImages.size());

	VkDescriptorPool descriptorPool;
	if (vkCreateDescriptorPool(logicalDevice, &poolInfo, NULL, &descriptorPool) != VK_SUCCESS)
	{
		throw std::runtime_error(" failed to create descriptor pool !");
	}

	std::vector layouts(swapChainImages.size(), descriptorSetLayout);
	VkDescriptorSetAllocateInfo descAllocInfo = {};
	descAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
	descAllocInfo.descriptorPool = descriptorPool;
	descAllocInfo.descriptorSetCount = static_cast(swapChainImages.size());
	descAllocInfo.pSetLayouts = layouts.data();

	std::vector descriptorSets;
	descriptorSets.resize(swapChainImages.size());
	if (vkAllocateDescriptorSets(logicalDevice, &descAllocInfo, descriptorSets.data()) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to allocate descriptor sets . ");
	}

	for (size_t i = 0; i < swapChainImages.size(); i++)
	{
		VkDescriptorBufferInfo bufferInfo = {};
		bufferInfo.buffer = uniformBuffers[i];
		bufferInfo.offset = 0;
		bufferInfo.range = sizeof(UniformBufferObject);

		VkWriteDescriptorSet descriptorWrite = {};
		descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
		descriptorWrite.dstSet = descriptorSets[i];
		descriptorWrite.dstBinding = 0;
		descriptorWrite.dstArrayElement = 0;
		descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
		descriptorWrite.descriptorCount = 1;
		descriptorWrite.pBufferInfo = &bufferInfo;
		descriptorWrite.pImageInfo = NULL;
		descriptorWrite.pTexelBufferView = NULL;
		vkUpdateDescriptorSets(logicalDevice, 1, &descriptorWrite, 0, NULL);
	}

完成了这些,在渲染的CommandBuffer中加入:

	vkCmdBindDescriptorSets(commandBuffer[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i], 0, NULL);	

这个指令完成资源的绑定,注意直接绑定的对象是descriptorSet。

此时CommandBuffer的命令全貌:

	for (size_t i = 0; i < commandBuffer.size(); i++)
	{
		VkCommandBufferBeginInfo beginInfo = {};
		beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
		beginInfo.flags = 0;

		if (vkBeginCommandBuffer(commandBuffer[i], &beginInfo) != VK_SUCCESS) {
			throw std::runtime_error("failed to begin recording command buffer . ");
		}

		VkRenderPassBeginInfo renderPassBeginInfo = {};
		renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
		renderPassBeginInfo.renderPass = renderPass;
		renderPassBeginInfo.framebuffer = swapChainFrameBuffer[i];
		renderPassBeginInfo.renderArea.offset = { 0 , 0 };
		renderPassBeginInfo.renderArea.extent = swapChainCreateInfo.imageExtent;

		std::array clearValues = {};
		clearValues[0].color = { 0.0f, 0.0f, 0.0f, 1.0f };
		clearValues[1].depthStencil = { 1.0f, 0 };

		renderPassBeginInfo.clearValueCount = clearValues.size();
		renderPassBeginInfo.pClearValues = clearValues.data();

		vkCmdBeginRenderPass(commandBuffer[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
		vkCmdBindPipeline(commandBuffer[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
		VkBuffer vertex_buffers[] = { vertexBuffer };
		VkDeviceSize offsets[] = { 0 };
		vkCmdBindVertexBuffers(commandBuffer[i], 0, 1, vertex_buffers, offsets);
		vkCmdBindIndexBuffer(commandBuffer[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);
		vkCmdBindDescriptorSets(commandBuffer[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i], 0, NULL);
		vkCmdDrawIndexed(commandBuffer[i], indices.size(), 1, 0, 0, 0);
		vkCmdEndRenderPass(commandBuffer[i]);

		if (vkEndCommandBuffer(commandBuffer[i]) != VK_SUCCESS)
		{
			throw std::runtime_error("failed to record command buffer . ");
		}

	}

4.配置深度缓冲

到目前为止,我们的程序结果是这样的:
【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形_第4张图片
但是这样的结果是不对的,因为红色的矩形的深度值是小于绿色的,它应该在绿色的矩形前面才对。这是没有配置深度缓冲的缘故,红色矩形后绘制,它就被先绘制的绿色矩形覆盖了。那么下面我们就来创建并配置深度缓冲。
深度缓冲作为RenderPass的一个Attachment,它的资源格式应该为VkImage,考虑到创建VkImage也都具有很多相似的代码结构,于是添加这样一个函数:

void createImage(uint32_t width,
	uint32_t height,
	VkFormat format,
	VkImageTiling tiling,
	VkImageUsageFlags usage,
	VkMemoryPropertyFlags properties,
	VkImage& image,
	VkDeviceMemory& imageMemory) 
{
	VkImageCreateInfo imageInfo = {};
	imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
	imageInfo.imageType = VK_IMAGE_TYPE_2D;
	imageInfo.extent.width = width;
	imageInfo.extent.height = height;
	imageInfo.extent.depth = 1;
	imageInfo.mipLevels = 1;
	imageInfo.arrayLayers = 1;
	imageInfo.format = format;
	imageInfo.tiling = tiling;
	imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	imageInfo.usage = usage;
	imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
	imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

	if (vkCreateImage(logicalDevice, &imageInfo, NULL, &image) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create image ! ");
	}

	VkMemoryRequirements memRequirements;
	vkGetImageMemoryRequirements(logicalDevice, image, &memRequirements);

	VkMemoryAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
	allocInfo.allocationSize = memRequirements.size;

	VkPhysicalDeviceMemoryProperties memProperties;
	vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);

	int memInd = -1;
	for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
	{
		if ( ( memRequirements.memoryTypeBits  & ( 1 << i )) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
		{
			memInd = i;
			break;
		}
	}
	if (memInd == -1)
	{
		throw std::runtime_error(" failed to find suitable memory type . ");
	}
	allocInfo.memoryTypeIndex = memInd;

	if (vkAllocateMemory(logicalDevice, &allocInfo, NULL, &imageMemory) != VK_SUCCESS)
	{
		throw std::runtime_error(" failed to allocate image memory .");
	}

	vkBindImageMemory(logicalDevice, image, imageMemory, 0);
}

函数的参数中有两个值得说明:VkImageTiling tiling 和 VkImageUsageFlags usage
VkImageTiling有两个取值:
VK_IMAGE_TILING_LINEAR:GPU读取Image时使用的是线性的布局
VK_IMAGE_TILING_OPTIMAL:GPU读取Image时使用的是优化后的布局
当CPU读取Image的时候并不受到这个参数的影响,那么这两者有什么区别呢?考虑现在GPU需要做一个滤波,它可能会用到某个像素(体素)周围的几个像素(体素)的信息,那么如果采用线性的布局,就会导致做一次滤波需要读取多行的数据,这对Cache是不友好的,而如果采用优化后的布局,GPU就能够充分利用Cache高效地使用Image。

VkImageUsageFlags usage:它的取值如下,主要描述Image的用途
【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形_第5张图片
这个函数主体与创建Buffer比较相似,就不赘述了。

然后创建ImageView部分也抽象出来成为一个函数:

VkImageView createImageView(VkImage image,
	VkFormat format,
	VkImageAspectFlags aspectFlags) 
{
	VkImageViewCreateInfo viewInfo = {};
	viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
	viewInfo.image = image;
	viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
	viewInfo.format = format;
	viewInfo.subresourceRange.aspectMask = aspectFlags;
	viewInfo.subresourceRange.baseMipLevel = 0;
	viewInfo.subresourceRange.levelCount = 1;
	viewInfo.subresourceRange.baseArrayLayer = 0;
	viewInfo.subresourceRange.layerCount = 1;

	VkImageView imageView;
	if (vkCreateImageView(logicalDevice, &viewInfo, NULL, &imageView) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create texture image view .");
	}
	
	return imageView;
}

VkImageAspectFlags aspectFlags用于选取Image的用途,一个Image可能会有多个不同的用途,那么这个参数就用于选取它的Usage中的哪一些将被使用。
在Vulkan Spec中有这么一段描述:

When using an image view of a depth/stencil image to populate a descriptor set (e.g. for sampling in the shader, or for use as an input attachment), the aspectMask must only include one bit and selects whether the image view is used for depth reads (i.e. using a floating-point sampler or input attachment in the shader) or stencil reads (i.e. using an unsigned integer sampler or input attachment in the shader). When an image view of a depth/stencil image is used as a depth/stencil framebuffer attachment, the aspectMask is ignored and both depth and stencil image subresources are used.

对于一个Depth/Stencil Image,如果需要它只会被用来depth read或者stencil read,那么aspectMask就只能取一个值,而如果它是被同时用作Depth/Stencil,这个参数可以直接忽略。
【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形_第6张图片

有了这两个参数就可以很方便地创建DepthImage和DepthImageView了:

	// create Depth Image View 
	createImage(actualExtent.width, actualExtent.height, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
	depthImageView = createImageView(depthImage, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_ASPECT_DEPTH_BIT);

资源创建好了以后,需要通过RenderPass把它作为Depth/Stencil Attachment ,修改7.7.8:

	//7.7.8 create render pass
	VkAttachmentDescription colorAttachment = {};
	colorAttachment.format = swapChainCreateInfo.imageFormat;
	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;

	VkAttachmentDescription depthAttachment = {};
	depthAttachment.format = VK_FORMAT_D24_UNORM_S8_UINT;
	depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
	depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
	depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
	depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
	depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

	VkAttachmentReference colorAttachmentRef = {};
	colorAttachmentRef.attachment = 0;
	colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

	VkAttachmentReference depthAttachmentRef = {};
	depthAttachmentRef.attachment = 1;
	depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

	std::vector attachments = { colorAttachment , depthAttachment };
	VkSubpassDescription subpass = {};
	subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
	subpass.colorAttachmentCount = 1;
	subpass.pColorAttachments = &colorAttachmentRef;
	subpass.pDepthStencilAttachment = &depthAttachmentRef;

	VkRenderPass renderPass;
	VkRenderPassCreateInfo renderPassInfo = {};
	renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
	renderPassInfo.attachmentCount = attachments.size();
	renderPassInfo.pAttachments = attachments.data();
	renderPassInfo.subpassCount = 1;
	renderPassInfo.pSubpasses = &subpass;
	renderPassInfo.dependencyCount = 0;
	renderPassInfo.pDependencies = NULL;

	if (vkCreateRenderPass(logicalDevice, &renderPassInfo, NULL, &renderPass) != VK_SUCCESS) {
		throw std::runtime_error("failed to create render pass!");
	}


主要是加入了一个VkAttachmentDescription用于描述Depth/Stencil Attachment,然后略微修改一下RenderPass的Attachment配置即可。

至此仍不够,此前配置Pipeline时:pipelineInfo.pDepthStencilState = NULL;
这里用到了深度缓冲,所以我们需要指定一个DepthStencilState:

	VkPipelineDepthStencilStateCreateInfo depthStencil = {};
	depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
	depthStencil.depthTestEnable = VK_TRUE;
	depthStencil.depthWriteEnable = VK_TRUE;
	depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
	depthStencil.depthBoundsTestEnable = VK_FALSE;
	depthStencil.minDepthBounds = 0.0f; // Optional
	depthStencil.maxDepthBounds = 1.0f; // Optional
	depthStencil.stencilTestEnable = VK_FALSE;
	depthStencil.front = {}; // Optional
	depthStencil.back = {}; // Optional
	pipelineInfo.pDepthStencilState = &depthStencil;

这个结构主要用于对深度/模板缓冲的处理策略,关于模板缓冲这里先略过,深度缓冲主要就是指定一下深度测试比较的方法,以及是否开启深度写入、深度测试。

————————————————————————————————————————————————————

完成以上步骤后,就能够正确地绘制这两个重叠的矩形了,和文章开头处的图一样。
这篇主要是对前一篇做一定的补充和修改,我就没有仔细地去标号了,详情可以参考全部源码:

#define GLFW_INCLUDE_VULKAN
#define GLFW_EXPOSE_NATIVE_WIN32

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

GLFWwindow* window;
VkInstance instance;
std::vector validationLayerNames = { "VK_LAYER_LUNARG_object_tracker" };
const float width = 800;
const float height = 600;
VkSemaphore imageAvailableSemaphore;
VkSemaphore renderFinishedSemaphore;
VkQueue graphicsQueue;
VkQueue presentQueue;
VkPhysicalDevice physicalDevice;
VkDevice logicalDevice;
VkRenderPassCreateInfo renderPassInfo = {};
VkSwapchainKHR swapChain;
std::vector commandBuffer;
VkCommandPool commandPool;

VkImage depthImage;
VkDeviceMemory depthImageMemory;
VkImageView depthImageView;

struct Vertex {
	float pos[3];
	float color[3];

	static VkVertexInputBindingDescription getBindingDescription() {
		VkVertexInputBindingDescription bindingDescription = {};
		bindingDescription.binding = 0;
		bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
		bindingDescription.stride = sizeof(Vertex);
		return bindingDescription;
	}

	static std::array getAttributeDescription() {
		std::array attributeDescriptions = {};
		attributeDescriptions[0].binding = 0;
		attributeDescriptions[0].location = 0;
		attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[0].offset = offsetof(Vertex , pos);

		attributeDescriptions[1].binding = 0;
		attributeDescriptions[1].location = 1;
		attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
		attributeDescriptions[1].offset = offsetof(Vertex, color);
		return attributeDescriptions;
	}
};

struct UniformBufferObject
{
	glm::mat4 model;
	glm::mat4 view;
	glm::mat4 proj;
}ubo;

const std::vector vertices = {
	{{-0.2f, -0.2f , 0.5f}, {0.0f, 1.0f, 0.0f}},
	{{0.8f, -0.2f , 0.5f}, {0.0f, 1.0f, 0.0f}},
	{{0.8f, 0.8f , 0.5f}, {0.0f, 1.0f, 0.0f}},
	{{-0.2f, 0.8f , 0.5f}, {0.0f, 1.0f, 0.0f}},

	{{-0.5f, -0.5f , 0.4f}, {1.0f, 0.0f, 0.0f}},
	{{0.5f, -0.5f , 0.4f}, {1.0f, 0.0f, 0.0f}},
	{{0.5f, 0.5f , 0.4f}, {1.0f, 0.0f, 0.0f}},
	{{-0.5f, 0.5f , 0.4f}, {1.0f, 0.0f, 0.0f}}
};

const std::vector indices = {
	0, 2, 1, 3, 2, 0,
	4, 6, 5, 6, 4, 7
};

static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
	VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
	VkDebugUtilsMessageTypeFlagsEXT messageType,
	const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
	void* pUserData) {

	std::cout << "validation layer: " << pCallbackData->pMessage << std::endl;

	return VK_FALSE;
}

void CreateBuffer(
	VkDeviceSize size,
	VkBufferUsageFlags usage,
	VkMemoryPropertyFlags properties,
	VkBuffer& buffer,
	VkDeviceMemory& memory
) {
	VkBufferCreateInfo bufferInfo = {};
	bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
	bufferInfo.size = size;
	bufferInfo.usage = usage;
	bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

	if (vkCreateBuffer(logicalDevice, &bufferInfo, NULL, &buffer) != VK_SUCCESS)
	{
		throw std::runtime_error(" create buffer fault . ");
	}

	VkMemoryRequirements memory_requirements;
	vkGetBufferMemoryRequirements(logicalDevice, buffer, &memory_requirements);

	VkPhysicalDeviceMemoryProperties memProperties;
	vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
	
	int memInd = -1;
	for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
	{
		if ( ( memory_requirements.memoryTypeBits & ( 1 << i ) ) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
		{
			memInd = i;
			break;
		}
	}
	if (memInd == -1)
	{
		throw std::runtime_error("no suitable memory for vertex buffer . ");
	}

	VkMemoryAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
	allocInfo.allocationSize = memory_requirements.size;
	allocInfo.memoryTypeIndex = memInd;

	if (vkAllocateMemory(logicalDevice, &allocInfo, NULL, &memory) != VK_SUCCESS)
	{
		throw std::runtime_error(" allocate memory fault . ");
	}

	vkBindBufferMemory(logicalDevice, buffer, memory, 0);
}

void CopyBuffer(
	VkBuffer srcBuffer,
	VkBuffer dstBuffer,
	VkDeviceSize size,
	VkDeviceSize srcOffset 
) {
	VkCommandBufferAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
	allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
	allocInfo.commandPool = commandPool;
	allocInfo.commandBufferCount = 1;

	VkCommandBuffer commandBuffer;
	vkAllocateCommandBuffers(logicalDevice, &allocInfo, &commandBuffer);

	VkCommandBufferBeginInfo beginInfo = {};
	beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
	beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

	vkBeginCommandBuffer(commandBuffer, &beginInfo);
	VkBufferCopy copyRegion = {};
	copyRegion.dstOffset = 0;
	copyRegion.srcOffset = srcOffset;
	copyRegion.size = size;
	vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);

	vkEndCommandBuffer(commandBuffer);

	VkSubmitInfo submitInfo = {};
	submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
	submitInfo.commandBufferCount = 1;
	submitInfo.pCommandBuffers = &commandBuffer;

	vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE );
	vkQueueWaitIdle(graphicsQueue);

	vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer);
}

void createImage(uint32_t width,
	uint32_t height,
	VkFormat format,
	VkImageTiling tiling,
	VkImageUsageFlags usage,
	VkMemoryPropertyFlags properties,
	VkImage& image,
	VkDeviceMemory& imageMemory) 
{
	VkImageCreateInfo imageInfo = {};
	imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
	imageInfo.imageType = VK_IMAGE_TYPE_2D;
	imageInfo.extent.width = width;
	imageInfo.extent.height = height;
	imageInfo.extent.depth = 1;
	imageInfo.mipLevels = 1;
	imageInfo.arrayLayers = 1;
	imageInfo.format = format;
	imageInfo.tiling = tiling;
	imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	imageInfo.usage = usage;
	imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
	imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

	if (vkCreateImage(logicalDevice, &imageInfo, NULL, &image) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create image ! ");
	}

	VkMemoryRequirements memRequirements;
	vkGetImageMemoryRequirements(logicalDevice, image, &memRequirements);

	VkMemoryAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
	allocInfo.allocationSize = memRequirements.size;

	VkPhysicalDeviceMemoryProperties memProperties;
	vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);

	int memInd = -1;
	for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
	{
		if ( ( memRequirements.memoryTypeBits  & ( 1 << i )) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
		{
			memInd = i;
			break;
		}
	}
	if (memInd == -1)
	{
		throw std::runtime_error(" failed to find suitable memory type . ");
	}
	allocInfo.memoryTypeIndex = memInd;

	if (vkAllocateMemory(logicalDevice, &allocInfo, NULL, &imageMemory) != VK_SUCCESS)
	{
		throw std::runtime_error(" failed to allocate image memory .");
	}

	vkBindImageMemory(logicalDevice, image, imageMemory, 0);
}

VkImageView createImageView(VkImage image,
	VkFormat format,
	VkImageAspectFlags aspectFlags) 
{
	VkImageViewCreateInfo viewInfo = {};
	viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
	viewInfo.image = image;
	viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
	viewInfo.format = format;
	viewInfo.subresourceRange.aspectMask = aspectFlags;
	viewInfo.subresourceRange.baseMipLevel = 0;
	viewInfo.subresourceRange.levelCount = 1;
	viewInfo.subresourceRange.baseArrayLayer = 0;
	viewInfo.subresourceRange.layerCount = 1;

	VkImageView imageView;
	if (vkCreateImageView(logicalDevice, &viewInfo, NULL, &imageView) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create texture image view .");
	}
	
	return imageView;
}

int main()
{
	// 1. Init Window
	glfwInit();
	glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
	glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
	window = glfwCreateWindow(width, height, "Vulkan window", nullptr, nullptr);

	// 2. check validation layer support 
	uint32_t layerCount;
	vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
	std::vector availableLayers(layerCount);
	vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());

	for (auto validationLayerName : validationLayerNames)
	{
		bool support = false;
		for (auto supportedLayerName : availableLayers)
		{
			if (strcmp(supportedLayerName.layerName, validationLayerName) == 0)
			{
				support = true;
				break;
			}
		}
		if (support == false)
		{
			std::cout << validationLayerName << " is not supported . " << std::endl;
			throw std::runtime_error(" not all validation layer is supported . ");
		}
	}


	// 3. Create Instance
	// 3.1 fill the application info
	VkApplicationInfo appInfo = {};
	appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
	appInfo.pApplicationName = "Hello Triangle";
	appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
	appInfo.pEngineName = "No Engine";
	appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
	appInfo.apiVersion = VK_API_VERSION_1_0;
	appInfo.pNext = NULL;

	// 3.2 get required extensions 
	uint32_t glfwExtensionCount = 0;
	const char** glfwExtensions;
	glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

	// 3.3 add validation layer required extension 
	std::vector required_extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
	required_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);

	// 3.4 fill the instance create info and create
	VkInstanceCreateInfo instanceCreateInfo = {};
	instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
	instanceCreateInfo.pApplicationInfo = &appInfo;
	instanceCreateInfo.enabledExtensionCount = static_cast(required_extensions.size());
	instanceCreateInfo.ppEnabledExtensionNames = required_extensions.data();
	instanceCreateInfo.enabledLayerCount = validationLayerNames.size();
	instanceCreateInfo.ppEnabledLayerNames = validationLayerNames.data();

	if (vkCreateInstance(&instanceCreateInfo, nullptr, &instance) != VK_SUCCESS) {
		throw std::runtime_error("failed to create instance!");
	}

	// 3.5 set up debug messenger
	VkDebugUtilsMessengerCreateInfoEXT messengerCreateInfo = {};
	messengerCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
	messengerCreateInfo.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;
	messengerCreateInfo.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;
	messengerCreateInfo.pfnUserCallback = debugCallback;
	messengerCreateInfo.pUserData = nullptr;

	auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
	if (func == NULL)
	{
		throw std::runtime_error("get vkCreateDebugUtilsMessengerEXT fault.");
	}
	VkDebugUtilsMessengerEXT debugMessenger;
	if (func(instance, &messengerCreateInfo, NULL, &debugMessenger) != VK_SUCCESS)
	{
		throw std::runtime_error("set debug messenger fault . ");
	}

	// 4. create physical device

	// 4.1 enumerate all physical device 
	physicalDevice = VK_NULL_HANDLE;
	uint32_t deviceCount = 0;
	vkEnumeratePhysicalDevices(instance, &deviceCount, NULL);
	if (deviceCount == 0)
	{
		throw std::runtime_error("failed to find physical device .");
	}
	std::vector devices(deviceCount);
	vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

	// 4.2 choose a suitable physical device 
	for (const auto& device : devices)
	{
		VkPhysicalDeviceProperties deviceProperties;
		VkPhysicalDeviceFeatures deviceFeatures;
		vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
		vkGetPhysicalDeviceProperties(device, &deviceProperties);

		if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
			deviceFeatures.geometryShader)
		{
			physicalDevice = device;
			break;
		}
	}

	if (physicalDevice == VK_NULL_HANDLE)
	{
		throw std::runtime_error("no suitable device.");
	}

	// 5. create surface 
	VkSurfaceKHR surface;
	VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {};
	surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
	surfaceCreateInfo.hwnd = glfwGetWin32Window(window);
	surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);

	if (vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface))
	{
		throw std::runtime_error(" failed to create window surface .");
	}

	// 6. prepare for creating device queue
	// 6.1 find a queueFamily which can be a graphics queue and a present queue
	uint32_t queueFamilyCount = 0;
	vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, NULL);
	std::vector queueFamilies(queueFamilyCount);
	vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());

	int i = 0;
	int queue_ind = -1;

	for (const auto& queueFamily : queueFamilies) {
		VkBool32 presentSupport = false;
		vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &presentSupport);
		if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT && presentSupport)
		{
			queue_ind = i;
		}
		i++;
	}

	if (queue_ind == -1) {
		throw std::runtime_error("No suitable queue .");
	}

	// 6.2 fill VkDeviceQueueCreateInfo 
	VkDeviceQueueCreateInfo queueCreateInfo = {};
	queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
	queueCreateInfo.queueFamilyIndex = queue_ind;
	queueCreateInfo.queueCount = 1;
	float queuePriority = 1.0f;
	queueCreateInfo.pQueuePriorities = &queuePriority;

	// 7. create logical device and use it to create some resources 

	// 7.1 check whether the physical device support the required extensions 
	const std::vector requireExtensions = {
		VK_KHR_SWAPCHAIN_EXTENSION_NAME
	};

	uint32_t availableExtensionCount;
	vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &availableExtensionCount, NULL);
	std::vector availableExtensions(availableExtensionCount);
	vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &availableExtensionCount, availableExtensions.data());

	std::set requireExtensionNames(requireExtensions.begin(), requireExtensions.end());
	for (const auto& extension : availableExtensions)
	{
		requireExtensionNames.erase(extension.extensionName);
	}

	if (!requireExtensionNames.empty()) {
		throw std::runtime_error("extension not fulfill");
	}

	// 7.2 create logical device 
	VkPhysicalDeviceFeatures deviceFeatures = {};
	VkDeviceCreateInfo deviceCreateInfo = {};
	deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
	deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
	deviceCreateInfo.queueCreateInfoCount = 1;
	deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
	deviceCreateInfo.enabledExtensionCount = requireExtensions.size();
	deviceCreateInfo.ppEnabledExtensionNames = requireExtensions.data();

	if (vkCreateDevice(physicalDevice, &deviceCreateInfo, NULL, &logicalDevice) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create logical device.");
	}

	// 7.3 retrieve device queue by logical device 
	vkGetDeviceQueue(logicalDevice, queue_ind, 0, &graphicsQueue);

	// 7.4 create swap chain by logical device 
	struct SwapChainSupportDetails {
		VkSurfaceCapabilitiesKHR capabilities;
		std::vector formats;
		std::vector presentModes;
	};

	SwapChainSupportDetails details;
	vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &details.capabilities);

	// 7.4.1 choose the surface format and present mode 
	uint32_t formatCount;
	vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL);
	if (formatCount != 0) {
		details.formats.resize(formatCount);
		vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, details.formats.data());
	}

	uint32_t presentModeCount;
	vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL);
	if (presentModeCount != 0) {
		details.presentModes.resize(presentModeCount);
		vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, details.presentModes.data());
	}

	if (formatCount == 0 || presentModeCount == 0) {
		throw std::runtime_error(" no suitable format or present mode ");
	}

	int format_ind = -1;
	for (const auto& availableFormat : details.formats)
	{
		format_ind++;
		if (availableFormat.format == VK_FORMAT_R8G8B8A8_UNORM
			&& availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
		{
			break;
		}
	}

	int present_mode_ind = -1;
	for (const auto& availablePresentMode : details.presentModes) {
		present_mode_ind++;
		if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
			break;
		}
	}

	// 7.4.2 choose the extent 
	VkExtent2D actualExtent;
	actualExtent.width = std::fmax(details.capabilities.minImageExtent.width,
		std::fmin(details.capabilities.maxImageExtent.width, width));
	actualExtent.height = std::fmax(details.capabilities.minImageExtent.height,
		std::fmin(details.capabilities.maxImageExtent.height, height));
	uint32_t imageCount = details.capabilities.minImageCount + 1;

	if (details.capabilities.maxImageCount > 0 && imageCount > details.capabilities.maxImageCount) {
		imageCount = details.capabilities.maxImageCount;
	}

	// 7.4.3 fill the VkSwapchainCreateInfoKHR and create .
	VkSwapchainCreateInfoKHR swapChainCreateInfo = {};
	swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
	swapChainCreateInfo.surface = surface;
	swapChainCreateInfo.minImageCount = imageCount;
	swapChainCreateInfo.imageFormat = details.formats[format_ind].format;
	swapChainCreateInfo.imageColorSpace = details.formats[format_ind].colorSpace;
	swapChainCreateInfo.imageExtent = actualExtent;
	swapChainCreateInfo.imageArrayLayers = 1;
	swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
	swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
	swapChainCreateInfo.preTransform = details.capabilities.currentTransform;
	swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
	swapChainCreateInfo.presentMode = details.presentModes[present_mode_ind];
	swapChainCreateInfo.clipped = VK_TRUE;
	swapChainCreateInfo.oldSwapchain = VK_NULL_HANDLE;

	if (vkCreateSwapchainKHR(logicalDevice, &swapChainCreateInfo, NULL, &swapChain) != VK_SUCCESS) {
		throw std::runtime_error("swap chain create fault ");
	}

	//7.5 create swapchain ImageView 
	std::vector swapChainImages;
	vkGetSwapchainImagesKHR(logicalDevice, swapChain, &imageCount, NULL);
	swapChainImages.resize(imageCount);
	vkGetSwapchainImagesKHR(logicalDevice, swapChain, &imageCount, swapChainImages.data());

	std::vector swapChainImageViews;
	swapChainImageViews.resize(swapChainImages.size());
	for (size_t i = 0; i < swapChainImages.size(); i++) {
		VkImageViewCreateInfo createInfo = {};
		createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
		createInfo.image = swapChainImages[i];
		createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
		createInfo.format = swapChainCreateInfo.imageFormat;
		createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
		createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
		createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
		createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
		createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
		createInfo.subresourceRange.baseMipLevel = 0;
		createInfo.subresourceRange.levelCount = 1;
		createInfo.subresourceRange.baseArrayLayer = 0;
		createInfo.subresourceRange.layerCount = 1;

		if (vkCreateImageView(logicalDevice, &createInfo, NULL, &swapChainImageViews[i]) != VK_SUCCESS) {
			throw std::runtime_error("failed to create image view.");
		}
	}



	//7.6 create shader module

	auto loadShaderByteCode = [](const std::string& fileName) {
		std::ifstream loadFile(fileName, std::ios::ate | std::ios::binary);
		if (!loadFile.is_open())
		{
			throw std::runtime_error("failed to open file!");
		}
		size_t fileSize = (size_t)loadFile.tellg();
		std::vector buffer(fileSize);
		loadFile.seekg(0);
		loadFile.read(buffer.data(), fileSize);
		loadFile.close();
		return buffer;
	};

	auto vertShaderCode = loadShaderByteCode("vert.spv");
	auto fragShaderCode = loadShaderByteCode("frag.spv");

	auto 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(logicalDevice, &createInfo, NULL, &shaderModule) != VK_SUCCESS)
		{
			throw std::runtime_error("failed to create shader module .");
		}

		return shaderModule;
	};

	VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
	VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);

	// 7.7 create pipeline 
	// 7.7.1 prepare shader stage
	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 };

	//7.7.2 prepare vertex input
	//7.7.2.1 create vertex buffer ( Modified )
	VkDeviceSize vertSize = sizeof(Vertex) * vertices.size();
	VkDeviceSize indexSize = sizeof(indices[0]) * indices.size();

	VkBuffer vertexBuffer;
	VkDeviceMemory vertexBufferMemory;
	CreateBuffer(vertSize,
		VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
		vertexBuffer,
		vertexBufferMemory
	);

	VkBuffer indexBuffer;
	VkDeviceMemory indexBufferMemory;
	CreateBuffer(indexSize,
		VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
		indexBuffer,
		indexBufferMemory);

	VkBuffer stagingBuffer;
	VkDeviceMemory stagingBufferMemory;
	CreateBuffer(vertSize + indexSize,
		VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
		VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
		stagingBuffer,
		stagingBufferMemory);

	//7.7.2.3 fill the memory 
	void* data;
	vkMapMemory(logicalDevice , stagingBufferMemory , 0 , vertSize , 0 , &data );
	memcpy(data, vertices.data(), vertSize);
	memcpy((char*)data + vertSize, indices.data(), indexSize);
	vkUnmapMemory(logicalDevice, stagingBufferMemory);

	//7.7.2.4 create vertex input info 
	auto bindingDescription = Vertex::getBindingDescription();
	auto attributeDescription = Vertex::getAttributeDescription();
	VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
	vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
	vertexInputInfo.vertexBindingDescriptionCount = 1;
	vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
	vertexInputInfo.vertexAttributeDescriptionCount = attributeDescription.size();
	vertexInputInfo.pVertexAttributeDescriptions = attributeDescription.data();

	//7.7.3 binding uniform buffer
	std::vector uniformBuffers;
	std::vector uniformBuffersMemory;
	VkDeviceSize bufferSize = sizeof(UniformBufferObject);
	uniformBuffers.resize(swapChainImages.size());
	uniformBuffersMemory.resize(swapChainImages.size());

	for (size_t i = 0; i < swapChainImages.size(); i++)
	{
		CreateBuffer(bufferSize,
			VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
			VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
			uniformBuffers[i],
			uniformBuffersMemory[i]
		);
	}


	//7.7.3 prepare input assembly state
	VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
	inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
	inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
	inputAssembly.primitiveRestartEnable = VK_FALSE;

	//7.7.4 prepare viewport and scissor state
	VkViewport viewport = {};
	viewport.x = 0.0f;
	viewport.y = 0.0f;
	viewport.width = width;
	viewport.height = height;
	viewport.minDepth = 0.0f;
	viewport.maxDepth = 1.0f;

	VkRect2D scissor = {};
	scissor.offset = { 0 , 0 };
	scissor.extent = swapChainCreateInfo.imageExtent;

	VkPipelineViewportStateCreateInfo viewportState = {};
	viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
	viewportState.viewportCount = 1;
	viewportState.pViewports = &viewport;
	viewportState.scissorCount = 1;
	viewportState.pScissors = &scissor;

	//7.7.5 prepare rasterization state 
	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_COUNTER_CLOCKWISE;
	rasterizer.depthBiasEnable = VK_FALSE;
	rasterizer.depthBiasConstantFactor = 0.0f;
	rasterizer.depthBiasClamp = 0.0f;
	rasterizer.depthBiasSlopeFactor = 0.0f;

	//7.7.6 prepare multisample state 
	VkPipelineMultisampleStateCreateInfo multisampling = {};
	multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
	multisampling.sampleShadingEnable = VK_FALSE;
	multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
	multisampling.minSampleShading = 1.0f;
	multisampling.pSampleMask = nullptr;
	multisampling.alphaToCoverageEnable = VK_FALSE;
	multisampling.alphaToOneEnable = VK_FALSE;

	//7.7.7 prepare color blend state
	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;
	colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
	colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
	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;
	colorBlending.attachmentCount = 1;
	colorBlending.pAttachments = &colorBlendAttachment;
	colorBlending.blendConstants[0] = 0.0f;
	colorBlending.blendConstants[1] = 0.0f;
	colorBlending.blendConstants[2] = 0.0f;
	colorBlending.blendConstants[3] = 0.0f;

	//7.7.8 create render pass
	VkAttachmentDescription colorAttachment = {};
	colorAttachment.format = swapChainCreateInfo.imageFormat;
	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;

	VkAttachmentDescription depthAttachment = {};
	depthAttachment.format = VK_FORMAT_D24_UNORM_S8_UINT;
	depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
	depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
	depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
	depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
	depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

	VkAttachmentReference colorAttachmentRef = {};
	colorAttachmentRef.attachment = 0;
	colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

	VkAttachmentReference depthAttachmentRef = {};
	depthAttachmentRef.attachment = 1;
	depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

	std::vector attachments = { colorAttachment , depthAttachment };
	VkSubpassDescription subpass = {};
	subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
	subpass.colorAttachmentCount = 1;
	subpass.pColorAttachments = &colorAttachmentRef;
	subpass.pDepthStencilAttachment = &depthAttachmentRef;

	VkRenderPass renderPass;
	VkRenderPassCreateInfo renderPassInfo = {};
	renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
	renderPassInfo.attachmentCount = attachments.size();
	renderPassInfo.pAttachments = attachments.data();
	renderPassInfo.subpassCount = 1;
	renderPassInfo.pSubpasses = &subpass;
	renderPassInfo.dependencyCount = 0;
	renderPassInfo.pDependencies = NULL;

	if (vkCreateRenderPass(logicalDevice, &renderPassInfo, NULL, &renderPass) != VK_SUCCESS) {
		throw std::runtime_error("failed to create render pass!");
	}

	//7.7.9 create pipeline layout 
	VkDescriptorSetLayoutBinding uboLayoutBinding = {};
	uboLayoutBinding.binding = 0;
	uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
	uboLayoutBinding.descriptorCount = 1;
	uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
	uboLayoutBinding.pImmutableSamplers = NULL;

	VkDescriptorSetLayout descriptorSetLayout;
	VkDescriptorSetLayoutCreateInfo layoutInfo = {};
	layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
	layoutInfo.bindingCount = 1;
	layoutInfo.pBindings = &uboLayoutBinding;

	if (vkCreateDescriptorSetLayout(logicalDevice, &layoutInfo, NULL, &descriptorSetLayout) != VK_SUCCESS)
	{
		throw std::runtime_error(" create descriptor set layout fault . ");
	}

	VkPipelineLayout pipelineLayout;
	VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
	pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
	pipelineLayoutInfo.setLayoutCount = 1;
	pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;

	if (vkCreatePipelineLayout(logicalDevice, &pipelineLayoutInfo, NULL, &pipelineLayout) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create graphics pipeline . ");
	}

	VkDescriptorPoolSize poolSize = {};
	poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
	poolSize.descriptorCount = static_cast(swapChainImages.size());
	
	VkDescriptorPoolCreateInfo poolInfo = {};
	poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
	poolInfo.poolSizeCount = 1;
	poolInfo.pPoolSizes = &poolSize;
	poolInfo.maxSets = static_cast(swapChainImages.size());

	VkDescriptorPool descriptorPool;
	if (vkCreateDescriptorPool(logicalDevice, &poolInfo, NULL, &descriptorPool) != VK_SUCCESS)
	{
		throw std::runtime_error(" failed to create descriptor pool !");
	}

	std::vector layouts(swapChainImages.size(), descriptorSetLayout);
	VkDescriptorSetAllocateInfo descAllocInfo = {};
	descAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
	descAllocInfo.descriptorPool = descriptorPool;
	descAllocInfo.descriptorSetCount = static_cast(swapChainImages.size());
	descAllocInfo.pSetLayouts = layouts.data();

	std::vector descriptorSets;
	descriptorSets.resize(swapChainImages.size());
	if (vkAllocateDescriptorSets(logicalDevice, &descAllocInfo, descriptorSets.data()) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to allocate descriptor sets . ");
	}

	for (size_t i = 0; i < swapChainImages.size(); i++)
	{
		VkDescriptorBufferInfo bufferInfo = {};
		bufferInfo.buffer = uniformBuffers[i];
		bufferInfo.offset = 0;
		bufferInfo.range = sizeof(UniformBufferObject);

		VkWriteDescriptorSet descriptorWrite = {};
		descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
		descriptorWrite.dstSet = descriptorSets[i];
		descriptorWrite.dstBinding = 0;
		descriptorWrite.dstArrayElement = 0;
		descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
		descriptorWrite.descriptorCount = 1;
		descriptorWrite.pBufferInfo = &bufferInfo;
		descriptorWrite.pImageInfo = NULL;
		descriptorWrite.pTexelBufferView = NULL;
		vkUpdateDescriptorSets(logicalDevice, 1, &descriptorWrite, 0, NULL);
	}


	VkPipelineDepthStencilStateCreateInfo depthStencil = {};
	depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
	depthStencil.depthTestEnable = VK_TRUE;
	depthStencil.depthWriteEnable = VK_TRUE;
	depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
	depthStencil.depthBoundsTestEnable = VK_FALSE;
	depthStencil.minDepthBounds = 0.0f; // Optional
	depthStencil.maxDepthBounds = 1.0f; // Optional
	depthStencil.stencilTestEnable = VK_FALSE;
	depthStencil.front = {}; // Optional
	depthStencil.back = {}; // Optional

	// 7.7.10 merge all the state and create graphics pipeline 

	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 = &depthStencil;
	pipelineInfo.pColorBlendState = &colorBlending;
	pipelineInfo.pDynamicState = NULL;
	pipelineInfo.renderPass = renderPass;
	pipelineInfo.subpass = 0;
	pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
	pipelineInfo.layout = pipelineLayout;

	VkPipeline graphicsPipeline;
	if (vkCreateGraphicsPipelines(logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &graphicsPipeline)
		!= VK_SUCCESS)
	{
		throw std::runtime_error("failed to create graphics pipeline");
	}

	vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr);
	vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr);

	VkCommandPoolCreateInfo poolCreateInfo = {};
	poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
	poolCreateInfo.queueFamilyIndex = queue_ind;
	poolCreateInfo.flags = 0;

	if (vkCreateCommandPool(logicalDevice, &poolCreateInfo, NULL, &commandPool) != VK_SUCCESS)
	{
		throw std::runtime_error("create command pool fault . ");
	}

	// create Depth Image View 
	createImage(actualExtent.width, actualExtent.height, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
	depthImageView = createImageView(depthImage, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_ASPECT_DEPTH_BIT);

	//7.7.11 create frame buffer 
	std::vector swapChainFrameBuffer;
	swapChainFrameBuffer.resize(swapChainImageViews.size());
	for (size_t i = 0; i < swapChainImageViews.size(); i++)
	{
		VkImageView attachments[] = {
			swapChainImageViews[i] , 
			depthImageView
		};

		VkFramebufferCreateInfo frameBufferInfo = {};
		frameBufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
		frameBufferInfo.renderPass = renderPass;
		frameBufferInfo.width = width;
		frameBufferInfo.height = height;
		frameBufferInfo.layers = 1;
		frameBufferInfo.pAttachments = attachments;
		frameBufferInfo.attachmentCount = 2;

		if (vkCreateFramebuffer(logicalDevice, &frameBufferInfo, NULL, &swapChainFrameBuffer[i]) != VK_SUCCESS)
		{
			throw std::runtime_error("create frame buffer fault . ");
		}
	}

	//7.7.12 create command buffer 
	commandBuffer.resize(swapChainFrameBuffer.size());

	VkCommandBufferAllocateInfo allocInfo = {};
	allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
	allocInfo.commandPool = commandPool;
	allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
	allocInfo.commandBufferCount = (uint32_t)commandBuffer.size();

	if (vkAllocateCommandBuffers(logicalDevice, &allocInfo, commandBuffer.data()) != VK_SUCCESS)
	{
		throw std::runtime_error("alloc command buffer fault . ");
	}

	for (size_t i = 0; i < commandBuffer.size(); i++)
	{
		VkCommandBufferBeginInfo beginInfo = {};
		beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
		beginInfo.flags = 0;

		if (vkBeginCommandBuffer(commandBuffer[i], &beginInfo) != VK_SUCCESS) {
			throw std::runtime_error("failed to begin recording command buffer . ");
		}

		VkRenderPassBeginInfo renderPassBeginInfo = {};
		renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
		renderPassBeginInfo.renderPass = renderPass;
		renderPassBeginInfo.framebuffer = swapChainFrameBuffer[i];
		renderPassBeginInfo.renderArea.offset = { 0 , 0 };
		renderPassBeginInfo.renderArea.extent = swapChainCreateInfo.imageExtent;

		std::array clearValues = {};
		clearValues[0].color = { 0.0f, 0.0f, 0.0f, 1.0f };
		clearValues[1].depthStencil = { 1.0f, 0 };

		renderPassBeginInfo.clearValueCount = clearValues.size();
		renderPassBeginInfo.pClearValues = clearValues.data();

		vkCmdBeginRenderPass(commandBuffer[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
		vkCmdBindPipeline(commandBuffer[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
		VkBuffer vertex_buffers[] = { vertexBuffer };
		VkDeviceSize offsets[] = { 0 };
		vkCmdBindVertexBuffers(commandBuffer[i], 0, 1, vertex_buffers, offsets);
		vkCmdBindIndexBuffer(commandBuffer[i], indexBuffer, 0, VK_INDEX_TYPE_UINT16);
		vkCmdBindDescriptorSets(commandBuffer[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i], 0, NULL);
		vkCmdDrawIndexed(commandBuffer[i], indices.size(), 1, 0, 0, 0);
		vkCmdEndRenderPass(commandBuffer[i]);

		if (vkEndCommandBuffer(commandBuffer[i]) != VK_SUCCESS)
		{
			throw std::runtime_error("failed to record command buffer . ");
		}

	}

	// 7.7.13 create semaphore
	VkSemaphoreCreateInfo semaphoreInfo = {};
	semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
	
	if (vkCreateSemaphore(logicalDevice, &semaphoreInfo, NULL, &imageAvailableSemaphore) != VK_SUCCESS ||
		vkCreateSemaphore(logicalDevice, &semaphoreInfo, NULL, &renderFinishedSemaphore) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create semaphore . ");
	}

	//7.7.14 copy memory 
	CopyBuffer(stagingBuffer, vertexBuffer, vertSize , 0 );
	CopyBuffer(stagingBuffer, indexBuffer, indexSize, vertSize);

	while (!glfwWindowShouldClose(window)) {
		glfwPollEvents();
		uint32_t imageIndex;

		uint64_t limit = (std::numeric_limits::max)();
		vkAcquireNextImageKHR(logicalDevice, swapChain, limit, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);

		VkSubmitInfo submitInfo = {};
		submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

		VkSemaphore waitSemaphores[] = { imageAvailableSemaphore };
		VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
		submitInfo.waitSemaphoreCount = 1;
		submitInfo.pWaitSemaphores = waitSemaphores;
		submitInfo.pWaitDstStageMask = waitStages;
		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &commandBuffer[imageIndex];

		VkSemaphore signalSemaphores[] = { renderFinishedSemaphore };
		submitInfo.signalSemaphoreCount = 1;
		submitInfo.pSignalSemaphores = signalSemaphores;

		static auto startTime = std::chrono::high_resolution_clock::now();
		auto currentTime = std::chrono::high_resolution_clock::now();
		float time = std::chrono::duration(currentTime - startTime).count();
		UniformBufferObject ubo = {};
		ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
		ubo.view = glm::lookAt(glm::vec3(0.0f, 0.0f, -5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
		ubo.proj = glm::perspective(glm::radians(45.0f), actualExtent.width / (float)actualExtent.height, 0.1f, 10.0f);
		ubo.proj[1][1] *= -1;
		void* data;
		vkMapMemory(logicalDevice, uniformBuffersMemory[imageIndex], 0, sizeof(ubo), 0, &data);
		memcpy(data, &ubo, sizeof(ubo));
		vkUnmapMemory(logicalDevice, uniformBuffersMemory[imageIndex]);

		if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
			throw std::runtime_error("failed to submit draw command buffer . ");
		}

		VkPresentInfoKHR presentInfo = {};
		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
		presentInfo.waitSemaphoreCount = 1;
		presentInfo.pWaitSemaphores = signalSemaphores;

		VkSwapchainKHR swapChains[] = { swapChain };
		presentInfo.swapchainCount = 1;
		presentInfo.pSwapchains = swapChains;
		presentInfo.pImageIndices = &imageIndex;
		presentInfo.pResults = NULL;
		vkQueuePresentKHR(graphicsQueue, &presentInfo);
	}

	//clean up 

	//clean command buffer
	vkFreeCommandBuffers(logicalDevice, commandPool, commandBuffer.size(), commandBuffer.data());
	vkDestroyCommandPool(logicalDevice, commandPool, nullptr);

	//clean frame buffer 
	for (auto framebuffer : swapChainFrameBuffer) {
		vkDestroyFramebuffer(logicalDevice, framebuffer, nullptr);
	}
	//clean uniform buffer
	for (auto uniformBuffer : uniformBuffers)
	{
		vkDestroyBuffer(logicalDevice, uniformBuffer, NULL);
	}
	for (auto uniformBufferMemory : uniformBuffersMemory)
	{
		vkFreeMemory(logicalDevice, uniformBufferMemory, NULL);
	}

	//clean depth image / buffer 
	vkDestroyImage(logicalDevice, depthImage, NULL);
	vkDestroyImageView(logicalDevice, depthImageView, NULL);
	vkFreeMemory(logicalDevice, depthImageMemory, NULL);

	//clean vertex buffer
	vkFreeMemory(logicalDevice, vertexBufferMemory , NULL );
	vkDestroyBuffer(logicalDevice, vertexBuffer, NULL);

	//clean index buffer
	vkFreeMemory(logicalDevice, indexBufferMemory, NULL);
	vkDestroyBuffer(logicalDevice, indexBuffer, NULL);

	//clean staging buffer
	vkFreeMemory(logicalDevice, stagingBufferMemory, NULL);
	vkDestroyBuffer(logicalDevice, stagingBuffer, NULL);

	//clean descriptor
	vkDestroyDescriptorSetLayout(logicalDevice, descriptorSetLayout, NULL);
	vkDestroyDescriptorPool(logicalDevice, descriptorPool, NULL);

	//clean semaphore
	vkDestroySemaphore(logicalDevice, renderFinishedSemaphore, nullptr);
	vkDestroySemaphore(logicalDevice, imageAvailableSemaphore, nullptr);

	//clean pipeline 
	vkDestroyPipeline(logicalDevice, graphicsPipeline, nullptr);
	vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr);

	//clean render pass
	vkDestroyRenderPass(logicalDevice, renderPass, nullptr);

	//clean swapchain
	for (auto imageView : swapChainImageViews) {
		vkDestroyImageView(logicalDevice, imageView, nullptr);
	}
	vkDestroySwapchainKHR(logicalDevice, swapChain, nullptr);

	//clean device
	vkDestroyDevice(logicalDevice, nullptr);
	auto destroyFunc = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
	if (destroyFunc != nullptr) {
		destroyFunc(instance, debugMessenger, NULL);
	}
	else {
		throw std::runtime_error(" not find function PFN_vkDestroyDebugUtilsMessengerEXT . ");
	}
	vkDestroySurfaceKHR(instance, surface, nullptr);
	vkDestroyInstance(instance, nullptr);

	glfwDestroyWindow(window);
	glfwTerminate();

	return 0;
}

你可能感兴趣的:(Vulkan)