Physical Devices

Vulkan实例初始化后,需要选择渲染时使用的显卡。

//获取显卡数量
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
//获取显卡信息
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
//选择适合该程序的GPU
for (const auto& device : devices) {
	if (isDeviceSuitable(device)) {
		physicalDevice = device;
		break;
//查询显卡属性,包括:名称,支持Vulkan的版本号
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
//支持纹理的最大值
deviceProperties.limits.maxImageDimension2D
//查询显卡特性,包括:纹理压缩,64位浮点述。多视口渲染(VR)
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
//是否支持几何着色器
deviceFeatures.geometryShader

Queue Families

  Vulkan中的每一个操作,从渲染到上传纹理,都需要将命令提交到队列中。不同的Queue Families只允许有一类命令子集。例如,一个队列只允许处理计算命令,或者一个队列只允许存在内存传输相关的命令

//选择的物理显卡Queue Family的数量
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
//Queue Family信息
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
//需要找到至少一个支持VK_QUEUE_GRAPHICS_BIT 的QueueFamily。
int i = 0;
for (const auto& queueFamily : queueFamilies) {
	//[10]寻找一个队列族,它能够链接window
	VkBool32 presentSupport = false;
	vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
	if (presentSupport) {
		indices.presentFamily = i;
	if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
		indices.graphicsFamily = i;
		if (indices.isComplete())
			break;
		i++;

Logical Device and Queues

  在选择要使用的物理设备(Physical Device)之后,我们需要设置一个 Logical Device接入物理设备。创建过程类似于实例创建过程。支持同一物理设备创建多个逻辑设备。(Vulkan的设计模式感觉与Filament很像)

//创建Queue信息
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
queueCreateInfo.queueCount = 1;
//Vulkan使用0.0到1.0之间的浮点数为队列分配优先级, 来进行缓冲区执行的调度。即使只有一个队列也需要设置
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;
//device features
VkPhysicalDeviceFeatures deviceFeatures = {};
//创建Device信息
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;
createInfo.pEnabledFeatures = &deviceFeatures;
//create a queue from both families
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
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);
//将队列信息加入驱动info
createInfo.queueCreateInfoCount =
	static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();
//开启扩展支持
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
//创建驱动
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
	throw std::runtime_error("failed to create logical device!");
//获取驱动队列,显示
//因为我们只创建一个队列,所以我们只使用索引0
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);

Window surface

  由于Vulkan是一个与平台无关的API,它本身不能直接调用窗口接口(window system)。为了在Vulkan和窗口系统之间建立连接,将渲染结果显示在屏幕上,我们需要使用WSI(window system integration)扩展。

//Windows的创建方法
VkWin32SurfaceCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.hwnd = glfwGetWin32Window(window);
createInfo.hinstance = GetModuleHandle(nullptr);
if (vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &surface) != VK_SUCCESS) {
	throw std::runtime_error("failed to create window surface!");
//Linux的创建方法与上面类似 vkCreateXcbSurfaceKHR
//VK_KHR_surface--VkSurfaceKHR对象,该对象为显示渲染图像的抽象类。
//使用GLFWWindow surface
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
	throw std::runtime_error("failed to create window surface!");

Swap Chain

  Vulkan没有“默认帧缓冲区”的概念,因此需要Swap Chain。其必须在Vulkan中显式创建。一般作用是使图像的显示与屏幕的刷新率同步。

Querying details of swap chain support

我们需要检查的基本属性有三种:

•基本surface 功能(图像的最小/最大数量,图像的最小/最大宽度和高度)

•surface 格式(像素格式、颜色空间)

•可用的渲染模式(presentation mode)

//查询扩展信息
uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
for(const auto& extension : availableExtensions) {
	requiredExtensions.erase(extension.extensionName);
//查询 swap chain支持
SwapChainSupportDetails details;
//basic surface capabilities 基本性能
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
//the supported surface formats
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount,	nullptr);
if (formatCount != 0) {
	details.formats.resize(formatCount);
	vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
//the supported presentation modes
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
if (presentModeCount != 0) {
	details.presentModes.resize(presentModeCount);
	vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
//在创建Logical Device时开启扩展支持
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();

Choosing the right settings for the swap chain

可以通过三种类型的设置来确定:

•Surface格式(颜色深度)

•呈现模式(将图像“交换”到屏幕的条件)

•交换范围(交换链中图像的分辨率)

  每个VkSurfaceFormatKHR均包含格式和colorSpace成员。 指定颜色通道和类型。 例如,VK_FORMAT_B8G8R8A8_SRGB表示以8位无符号整数的顺序存储B,G,R和alpha通道,每个像素总共32位。 colorSpace成员使用VK_COLOR_SPACE_SRGB_NONLINEAR_KHR是否支持SRGB颜色空间。 请注意,在旧版本中,此标志为VK_COLORSPACE_SRGB_NONLINEAR_KHR。

for (const auto& availableFormat : availableFormats) {
	if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB 
		&& availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
		return availableFormat;
//如果查询失败返回第一个
return availableFormats[0];

Presentation mode

Vulkan有四种显式可能的模式

  • VK_PRESENT_MODE_IMMEDIATE_KHR:应用程序提交的图像会立即传输到屏幕,这可能会导致撕裂。
  • VK_PRESENT_MODE_FIFO_KHR:
    swap chain是一个队列,当刷新显示时,显示从队列前面获取图像,程序在队列后面插入渲染图像。如果队列已满,则程序必须等待。这与现代游戏中的垂直同步最为相似。刷新显示的时刻称为“垂直空白”。
  • VK_PRESENT_MODE_FIFO_RELAXED_KHR:此模式仅在应用程序延迟且队列在最后一个空格处为空时与前一个模式不同。而不是等待下一个垂直空白,图像是立即转移时,它最终到达。这可能会导致撕裂
  • VK_PRESENT_MODE_MAILBOX_KHR:是第二种模式的另一种变体。队列已满时,不再阻塞应用程序,而是将已排队的映像替换为较新的映像。此模式可用于实现三重缓冲,这使您可以避免撕裂,而延迟问题明显小于使用双缓冲的标准垂直同步。
//三级缓存更好,如果有就开启
for (const auto& availablePresentMode : availablePresentModes) {
	if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
		 return availablePresentMode;
return VK_PRESENT_MODE_FIFO_KHR;

Swap extent

Swap extent是swap chain的图像分辨率

//在minImageExtent和maxImageExtent内选择与窗口最匹配的分辨率
if (capabilities.currentExtent.width != UINT32_MAX) {
	return capabilities.currentExtent;
else {
	VkExtent2D actualExtent = { WIDTH, HEIGHT };
	actualExtent.width = std::max(capabilities.minImageExtent.width,
			std::min(capabilities.maxImageExtent.width, actualExtent.width));
	actualExtent.height = std::max(capabilities.minImageExtent.height,
			std::min(capabilities.maxImageExtent.height, actualExtent.height));
		return actualExtent;

Creating the swap chain

SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); //swapChainSupport.capabilities.minImageCount + 1 //minImageCount意味着我们有时可能需要等待驱动程序完成内部操作, //然后才能获取另一个要渲染的图像。因此,建议请求至少比minImageCount多一个图像: //uint32_t imageCount = swapChainSupport.capabilities.minImageCount; uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { imageCount = swapChainSupport.capabilities.maxImageCount; VkSwapchainCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; createInfo.surface = surface; createInfo.minImageCount = imageCount; createInfo.imageFormat = surfaceFormat.format; createInfo.imageColorSpace = surfaceFormat.colorSpace; createInfo.imageExtent = extent; //imageArrayLayers指定每个图像包含的层的数量。除非开发3D应用程序,否则该值始终为1。 createInfo.imageArrayLayers = 1; createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; //两种方法可以处理从多个队列访问图像: //VK_SHARING_MODE_CONCURRENT //VK_SHARING_MODE_EXCLUSIVE QueueFamilyIndices indices = findQueueFamilies(physicalDevice); uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; if (indices.graphicsFamily != indices.presentFamily) { createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; createInfo.queueFamilyIndexCount = 2; createInfo.pQueueFamilyIndices = queueFamilyIndices; else { createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.queueFamilyIndexCount = 0; createInfo.pQueueFamilyIndices = nullptr; //指定对SwapChain中的图像应用某种变换 createInfo.preTransform = swapChainSupport.capabilities.currentTransform; //alpha channel should be used for blending createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; //窗口重置--取缓存区图像方式 createInfo.oldSwapchain = VK_NULL_HANDLE; if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); swapChainImageFormat = surfaceFormat.format; swapChainExtent = extent;

Retrieving the swap chain images

//检索swap chain中的VkImages
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
swapChainImages.resize(imageCount);
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());

Image views

   在渲染管线中使用VkImage就必须创建VkImageView对象。 实际上就是视图。 它描述了如何访问图像以及访问图像的哪一部分。

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];
	//选择视图类型 1D 2D 3D
	createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
	createInfo.format = swapChainImageFormat;
	//components字段允许旋转颜色通道。 
	createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; //default
	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(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
		throw std::runtime_error("failed to create image views!");
                    Vulkan学习(二):官方教程--(Physical Devices & Queue Families & Logical Device & Window Surface & Swap Chain)
                    学习目标:之前已经简单地讨论过,几乎在Vulkan中的每一个操作,从绘制到上传纹理,都需要将命令提交到队列中。
				
dEQP自述文件 该存储库包含一个称为dEQP(drawElements质量计划)的GPU测试套件。 dEQP包含针对多个图形API的测试,包括OpenGL ES,EGL和Vulkan。 dEQP的最新文档可在。 由一致性测试生成的.qpa日志可能包含结果的嵌入式PNG图像。 可以使用scripts/qpa_image_viewer.html来查看这些文件,方法是使用网络浏览器打开文件并按照其说明进行操作,或者使用工具。 Khronos Vulkan一致性测试 该存储库包括external/vulkancts目录下的Khronos Vulkan CTS。 有关更多信息,请参见 。 Khronos OpenGL / OpenGL ES一致性测试 该存储库包括external/openglcts目录下的Khronos OpenGL / OpenGL ES CTS。 有关更多信息,请参见 。 Android的角度 可以按照的说明为Android构建ANGLE。 可以使用--angle-path选项将生成的ANGLE共享对象库链接到dEQP.apk并将其嵌入。 这将导致dEQP.
Android上Vulkan的分步教程,请遵循 。 我们需要Android上的 ,可以。 在Android Studio中,只需将着色器代码放在src/main/shaders文件夹下,它们将被编译成.spv文件,并打包为资产。 柯南包装经理 根据设置柯南支持,要安装柯南,请运行pip install conan 。 https://github.com/KhronosGroup/Vulkan-Samples 官方git 有安装教程 https://codechina.csdn.net/mirrors/SaschaWillems/Vulkan https://vulkan-tutorial.com/ vulkan官方教程 SDK下载:https://vulkan.lunarg.com/ Vulkan安装相关: git clone --recursive https://gi
从包含六个不同面的磁盘加载立方体贴图纹理。所有面和 mip 级别都上传到视频内存中,立方体贴图显示在天空盒上作为背景,并显示在 3D 模型上作为反射。 并且演示了天空盒子的用法。 加载cubemap buildCommandBuffers loadAssets DescriptorSets 描述符 UniformBuffer 、Shader 1.skybox.vert 代码如下(示例): 2.skybox.frag 代码如下(示例): 3.reflect.vert 代码如下(示例): 译者注:示例代码点击此处 格式定义颜色分量的数量,每个组件的位数以及使用的数据类型。 在交换链创建期间,我们必须指定是否要使用带或不带alpha分量的红色,绿色和蓝色通道,是否应使用无符号整数或浮点数据类型对颜色值进行编码,以及它们的精度是什么。 我们还必须选择是否使用线性或非线性颜色空间对颜色值进行编码。 但与其他交换链参数一样,我们只能使用呈现表面支持的值。 做好准...
1. Vulkan 官方文档:https://www.khronos.org/vulkan/ 2. Vulkan 课程:https://www.youtube.com/watch?v=SC29n-bBkcw 3. Vulkan 入门教程:https://vulkan-tutorial.com/ 4. Vulkan 开发指南:https://developer.nvidia.com/vulkan-development-guide 5. Vulkan 开发者博客:https://gpuopen.com/gaming-product/vulkan-developer-blog/ 这些资料都是关于 Vulkan 的,应该能帮到你。如果你对某些概念不是很理解,也可以问我,我尽力帮你解答。
CSDN-Ada助手: 非常感谢博主分享这篇“曲线曲面基础”的博客,对我这种初学者来说非常有帮助。博客的内容通俗易懂、深入浅出,让我对曲线曲面的基本概念有了更加清晰的认识。希望博主能够持续创作,分享更多有价值的知识,我相信这些都会对广大读者产生巨大的帮助。再次感谢博主的付出和努力! 为了方便博主创作,提高生产力,CSDN上线了AI写作助手功能,就在创作编辑器右侧哦~(https://mp.csdn.net/edit?utm_source=blog_comment_recall )诚邀您来加入测评,到此(https://activity.csdn.net/creatActivity?id=10450&utm_source=blog_comment_recall)发布测评文章即可获得「话题勋章」,同时还有机会拿定制奖牌。 闭关之 C++ Template 笔记(一):PartⅠ基本概念(一) 很专业,很专业 Vulkan学习(八): Hello Triangle 重构 shawn·xiao: 编译一堆报错,是不是很多依赖、链接没有加进来? 闭关之 C++ 并发编程笔记(一):线程与锁 MrLiuWinter: 为什么不出一个笔记的汇总文章呢,专栏真的很少人看啊表情包 闭关之现代 C++ 笔记汇总(二):特性演化 非常感谢博主分享,支持大佬表情包