Gobal Tips: 0.此为本人学习Vulkan Programming Guide的笔记,所学习的书本翻译资料来自Blary的专栏和know yourself,十分感谢他们 1.函数中标记为[IN]的是输入值,标记为[OUT]的是返回值, 2.本人看不懂或者是不清楚的均不作介绍,以免误导他人,但日后会尽力补齐 3.本文面向包括作者在内的所有菜鸟级的开发者,所以所有的Demo代码均不精简,为的是读者能够直接复制使用减少出错 4.因为是笔记所以免不了摘抄,但本人都尽量按照自己的理解和实践去重新总结归纳过了,无意侵犯他人,如有,还请理解原谅
Vulkan包括一个层次结构的功能,如下图所示 Vulkan实例: 是一个软件构造,在逻辑上将应用程序的状态与在应用程序上下文中运行的其他应用程序或库分离。系统中的物理设备被呈现为实例的成员,每个实例具有某些能力,包括可用队列的选择。 物理设备: 通俗来讲就是你电脑的显卡 逻辑设备: 由Vulkan实例创建的逻辑设备是围绕物理设备的软件结构,并且表示与特定物理设备相关联的资源的预留。 这包括物理设备上可用队列的可能子集。可以在一个物理设备上创建的多个逻辑设备,您的应用程序将花费大部分时间与逻辑设备进行交互。
vkCreateInstance()函数成功,我们可以使用它来找到系统中安装的Vulkan兼容设备
但在我们可以创建[逻辑设备]之前,我们必须找到[物理设备]。
//调用vkEnumeratePhysicalDevices()函数来找到系统中安装的Vulkan兼容设备 VkResult vkEnumeratePhysicalDevices ( VkInstance instance,//[IN]传入[实例] uint32_t* pPhysicalDeviceCount,//[OUT]返回支持的设备数 VkPhysicalDevice* pPhysicalDevices//[OUT]返回支持的[物理设备]的数组 ); //这个函数希望你调用它两次, //第一次只需要将pPhysicalDevices设置为nullptr(尽管pPhysicalDeviceCount仍然必须是有效的指针) //获取pPhysicalDeviceCount的返回值,用来调整数组大小 //并且第二次把数组传入pPhysicalDevices,便可获得支持的[物理设备]的列表 //物理设备句柄用于查询设备的功能,并最终创建逻辑设备 void vkGetPhysicalDeviceProperties ( VkPhysicalDevice physicalDevice,//[IN]传入物理设备 VkPhysicalDeviceProperties* pProperties//[OUT]返回其属性 ); //[物理设备]的属性结构体 typedef struct VkPhysicalDeviceProperties { uint32_t apiVersion;//设备支持的最高版本的Vulkan uint32_t driverVersion;//包含用于控制设备的驱动程序的版本 uint32_t vendorID; uint32_t deviceID; VkPhysicalDeviceType deviceType; char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];//设备名,比如我的是 GeForce 940MX uint8_t pipelineCacheUUID[VK_UUID_SIZE]; VkPhysicalDeviceLimits limits; VkPhysicalDeviceSparseProperties sparseProperties; } VkPhysicalDeviceProperties;通俗讲就是你显卡的显存. 可用作纹理和其他数据的后备存储. 分为多种类型,每种类型都有一组属性. 每种类型的存储器然后由设备的堆之一支持,可能会有几个.
//[物理设备]内存属性查询函数 void vkGetPhysicalDeviceMemoryProperties ( VkPhysicalDevice physicalDevice, //[IN]传入[物理设备] VkPhysicalDeviceMemoryProperties* pMemoryProperties //[OUT]返回[物理设备]属性 ); //[物理设备]内存属性结构体 typedef struct VkPhysicalDeviceMemoryProperties { uint32_t memoryTypeCount;//内存类型数,最大值为VK_MAX_MEMORY_TYPES=32 VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES];//每种内存类型的属性的数组 uint32_t memoryHeapCount; //可用的堆数量,最大值为VK_MAX_MEMORY_HEAPS=16 VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS];//堆的属性数组 } VkPhysicalDeviceMemoryProperties; //内存类型属性结构体 typedef struct VkMemoryType { VkMemoryPropertyFlags propertyFlags; //描述了内存的类型 //通常flag都存储着一些位,位描述了所在结构的具体属性,这个字段将在下文介绍 uint32_t heapIndex; //此种内存类型对应的堆的下标 } VkMemoryType; //堆的属性的结构体 typedef struct VkMemoryHeap { VkDeviceSize size; //堆大小,以byte为单位 VkMemoryHeapFlags flags;//无需关心 } VkMemoryHeap;VkMemoryType结构体的propertyFlags字段由这些位构成: •VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT 表示内存是本地的(即物理连接到)设备。如果该位未设置,则可以假定存储器对于主机是本地的。 •VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT 表示使用的内存分配 此类型可以由主机映射和读取或写入。如果该位未被设置,则这种类型的存储器不能被主机直接访问,而是被设备独占使用。 •VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 表示当此类型的内存由主机和设备同时访问时,这些访问将在两个客户端之间一致。如果未设置此位,则设备或主机可能不会看到每个执行的写入的结果,直到高速缓存被显式刷新。 •VK_MEMORY_PROPERTY_HOST_CACHED_BIT 表示此类型的内存中的数据由主机缓存。对这种类型的存储器的读取访问通常比如果该位未被设置时的读取访问更快。然而,设备的访问可以具有略高的延迟,特别是如果存储器也是一致的。 •VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT 表示使用此类型分配的内存不一定立即从关联的堆消耗空间,并且驱动程序可能推迟物理内存分配,直到内存对象用于回退资源。
Vulkan设备执行提交到队列的工作。 每个设备将有一个或多个队列,并且每个队列将属于设备的队列系列之一。 [队列系列]是一组具有相同功能但能够并行运行的队列。 队列系列的数量,每个系列的能力以及属于每个系列的队列的数量都是物理设备的属性。
//[物理设备]的[队列系列]查询函数 void vkGetPhysicalDeviceQueueFamilyProperties ( VkPhysicalDevice physicalDevice,//[IN]传入[物理设备] uint32_t* pQueueFamilyPropertyCount,//[OUT]返回[物理设备]数 VkQueueFamilyProperties* pQueueFamilyProperties);//[OUT]返回[物理设备]的[队列系列]的数组 //这个函数希望你调用它两次,具体用法与vkEnumeratePhysicalDevices()类似 //通过第一次调用 //vkGetPhysicalDeviceQueueFamilyProperties(m_device,&m_queueFamilyPropertyCount,nullptr); //获取并确定自己[物理设备]的[队列系列]的数量m_queueFamilyPropertyCount //在第二次调用时,在pQueueFamilyProperties中传递此数组,Vulkan将返回[物理设备]的[队列系列]的属性。字段queueFlags所表示的队列的总体功能,由VkQueueFlagBits位的组合构成所描述 •如果设置了VK_QUEUE_GRAPHICS_BIT,则此系列中的队列支持图形操作,例如绘制点,线和三角形。 •如果设置了VK_QUEUE_COMPUTE_BIT,则此系列中的队列支持计算操作,例如调度计算着色器。 •如果设置了VK_QUEUE_TRANSFER_BIT,则此系列中的队列支持传输操作,例如复制缓冲区和映像内容。 •如果设置了VK_QUEUE_SPARSE_BINDING_BIT,则此系列中的队列支持用于更新稀疏资源的内存绑定操作。
timestampValidBits字段:暂暂时不作介绍,待日后补齐
minImageTimestampGranularity字段:暂时不作介绍,待日后补齐
Tips:事实上,Vulkan中的所有内容都被表示为一个由句柄引用的对象。 不管是我们创建的[vulkan实例]还是获取到的[物理设备]的实例其实都是它们的句柄,可以把句柄看做智能指针 vulkan中的句柄分为两大类: –1.可分派对象: 是内部包含调度表的对象。 这是各种组件使用的功能表,用于确定当您的应用程序调用Vulkan时要执行的代码部分。 这些类型的对象通常是较大较复杂的结构,目前由: 实例(VkInstance), 物理设备(VkPhysicalDevice), 逻辑设备(VkDevice), 命令缓冲区(VkCommandBuffer), 队列(VkQueue)组成) 构成。
–2.不可分散对象: 所有其他对象被认为是不可分散的。 任何Vulkan函数的第一个参数总是一个可调度的对象。 此规则的唯一例外是与实例的创建和初始化相关的功能。
在大多数情况下,这些对象与应用程序无关,仅影响API的结构以及系统级组件(如Vulkan加载器和层)与这些对象的互操作性。
层是Vulkan的特征,允许修改其行为。 层通常拦截全部或部分Vulkan,并添加诸如日志记录,跟踪,提供诊断,分析等功能。 可以在实例级别添加一个层,在这种情况下,它会影响整个Vulkan实例以及可能由其创建的每个设备。 或者,可以在设备级别添加该层,在这种情况下,该层仅影响其启用的设备。
//[vulkan实例]层查询函数 VkResult vkEnumerateInstanceLayerProperties ( uint32_t* pPropertyCount, //[OUT]返回[vulkan实例]层的数量 VkLayerProperties* pProperties//[OUT]返回[vulkan实例]层属性的数组 ); //这个函数希望你调用它两次 //第一次把pProperties设为nullptr,确定层的数量,并调整数组大小 //的二次再把数组传入 //层的属性结构体 typedef struct VkLayerProperties { char layerName[VK_MAX_EXTENSION_NAME_SIZE]; //层名字符串 uint32_t specVersion; //规格版本 uint32_t implementationVersion; //实现版本 char description[VK_MAX_DESCRIPTION_SIZE]; //此用于在用户界面中记录或显示 } VkLayerProperties; //应用程序写入器可以识别层的特定实现,并且仅当该实现的版本超过特定版本时选择使用它. //例如用于避免已经确认的关键错误。要启用实例级别的层,请将其名称先包含在用于创建实例的VkInstanceCreateInfo结构的ppEnabledLayerNames字段中,再对vkCreateInstance()进行调用
不仅在实例级别,层可以获取到层。 层也可以在设备级应用(实际上是用在[物理设备]的[逻辑设备]上)
//[物理]设备层查询函数 VkResult vkEnumerateDeviceLayerProperties ( VkPhysicalDevice physicalDevice,//[IN]传入物理设备 uint32_t* pPropertyCount,//[OUT]返回层数 VkLayerProperties* pProperties//[OUT]返回层数组 //同样由VkLayerProperties结构的实例描述 ); //接上文Demo.cpp的......处 //查询[物理设备]的层 uint32_t numDeviceLayers = 0; std::vector<VkLayerProperties> deviceLayerProperties; vkEnumerateDeviceLayerProperties(m_physicalDevices[0], &numDeviceLayers, nullptr); if (numDeviceLayers != 0) { std::cout << numDeviceLayers << std::endl;//打印层数 deviceLayerProperties.resize(numDeviceLayers); vkEnumerateDeviceLayerProperties(m_physicalDevices[0],&numDeviceLayers, deviceLayerProperties.data()); } //......为了在创建与系统中的[物理设备]相对应的[逻辑设备]时启用层,请在用于创建设备的VkDeviceCreateInfo的ppEnabledLayerNames成员中包含层名称,再对vkCreateDevice()进行调用 SDK中包含的层次: •VK_LAYER_LUNARG_api_dump将Vulkan调用及其参数和值打印到控制台。 •VK_LAYER_LUNARG_core_validation对描述符集,流水线状态和动态状态中使用的参数和状态执行验证;验证SPIR-V模块和图形管道之间的接口;并跟踪并验证用于返回对象的GPU内存的使用情况。 •VK_LAYER_LUNARG_device_limits确保将值传递给Vulkan命令作为参数或数据结构成员属于设备支持的功能集限制。 •VK_LAYER_LUNARG_image验证图像使用情况与支持的格式一致。 •VK_LAYER_LUNARG_object_tracker对Vulkan对象执行跟踪,尝试执行 捕获泄漏,使用后自由错误和其他无效对象使用。 •VK_LAYER_LUNARG_parameter_validation确认传递给Vulkan函数的所有参数值都有效。 •VK_LAYER_LUNARG_swapchain对第5章“演示文稿”中描述的WSI(Windows系统集成)扩展功能提供的功能进行验证。 •VK_LAYER_GOOGLE_threading确保了对于线程的Vulkan命令的有效使用,确保没有两个线程在不同时间访问同一个对象。 •VK_LAYER_GOOGLE_unique_objects确保每个对象都有一个唯一的句柄,以便应用程序更容易的跟踪,避免实现可能会重复使用相同参数表示对象的句柄。
VK_LAYER_LUNARG_standard_validation:暂不作介绍,待日后补充
暂不作介绍,待日后补充