vulkan是平台无关的,所以不能直接和窗口系统交互,而是使用WSI(window system integration)扩展。这里介绍的第一个扩展就是VK_KHR_surface,已经使用GLFW启用了,这个扩展暴露了一个VkSurfaceKHR对象作为要渲染的图像的抽象表示。

由于window surface的创建需要在vulkan实例创建之后立即进行,因此这个功能会影响到物理设备的选择。

Surface创建

VkSurfaceKHR对象和使用是平台无关的,但是其创建不是的,其创建依赖所在平台窗口系统的细节。但是我们可以利用GLFW来屏蔽掉平台相关的代码,使得代码变为平台无关的。

void createSurface() { 
	VkSurfaceKHR surface;
	if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { 
		throw std::runtime_error("failed to create window surface!"); 
	} 
}

在vulkan instance销毁之前销毁

vkDestroySurfaceKHR(instance, surface, nullptr);

设备选择

surface会影响到物理设备的选择。我们前面在2. physical devices与queue families当中选择设备的时候会选择支持图形绘制命令队列的物理设备,这里使用surface的话,还需要使用的设备额外支持一下surface展示。

struct QueueFamilyIndices {
	std::optional<uint32_t> graphicsFamily;
	std::optional<uint32_t> presentFamily;
	bool isComplete() {
	  return graphicsFamily.has_value() && presentFamily.has_value();
	}
};
 
/**
* 检查物理设备是否支持所需的扩展
*/
bool isDeviceSuitable(VkPhysicalDevice device) {
	QueueFamilyIndices indices = findQueueFamilies(device);
	return indices.isComplete();
}
 
  /**
   * 查找满足需求的队列族
   */
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
	QueueFamilyIndices indices;
	// 获取queue family的属性
	uint32_t queueFamilyCount = 0;
	vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount,
											 nullptr);
	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) {
		indices.graphicsFamily = i;
	  }
	  VkBool32 presentSupport = false;
	  // queue family是否支持surface
	  vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
	  if (presentSupport) {
		indices.presentFamily = i;
	  }
	
	  if (indices.isComplete()) {
		break;
	  }
	  i++;
	}
	return indices;
}

逻辑设备创建surface queue

3. logical device与queue创建逻辑设备的时候,加上surface要使用的queue

VkQueue presentQueue;
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
 
// 将原来创建逻辑设备使用的queue信息变为数组
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; 
std::set<uint32_t> uniqueQueueFamilies = {
	indices.graphicsFamily.value(), 
	indices.presentFamily.value()
}; 
float queuePriority = 1.0f; 
for (uint32_t queueFamily : uniqueQueueFamilies) { 
	VkDeviceQueueCreateInfo queueCreateInfo{}; 
	queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 
	queueCreateInfo.queueFamilyIndex = queueFamily; 
	queueCreateInfo.queueCount = 1; 
	queueCreateInfo.pQueuePriorities = &queuePriority; 
	queueCreateInfos.push_back(queueCreateInfo); 
}
// 以数组的形式设置
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()); 
createInfo.pQueueCreateInfos = queueCreateInfos.data();
 
// create logical device
// ...
 
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);