Understanding Vulkan Objects

本文详细解释了VulkanAPI中的关键对象类型,如Vk对象、实例、物理设备、设备、队列等,介绍了它们之间的关系、创建顺序和依赖性,以及如何通过DescriptorSet、DescriptorPool等进行资源管理和配置。
摘要由CSDN通过智能技术生成

​ 和学习其他API一样,学习Vulkan API中有一个重要部分:了解Vulkan API定义了拿下类型,以及这些类型之间的关系。为了帮助理解这些类型,接下来会绘制一幅关系图,表现它们之间的关系,尤其是创建依赖关系。

​ 每个Vulkan类型都有一个特定的前缀Vk。这些前缀在下面的关系图中将会被省略。每个Vulkan函数都有一个特定前缀vk。举个例子:关系图中的Sampler,其实表示VkSampler。这些Vulkan类型不能被视为指针或者数字,把它理解成句柄是一个不错的方向,它是一个不透明的句柄。绿色背景对象的原始类型是uint32_t

​ 带箭头的实线表示创建顺序。举个例子,创建DescriptorSet之前,必选先创建DescriptorPool

​ 带实心菱形的实线表示包含关系,也就是说,该对象无需创建,直接从它的父对象中获取。举个例子:PhysicalDevice对象并不是创建的,而是从Instance对象中枚举的。

​ 虚线表示其他关系,比如:提交各种命令到CommandBUffer对象。

在这里插入图片描述
InstanceVulkan应用第一个创建的对象。它用于连接上层应用与Vulkan运行时驱动,因此,在上层应用中应该只创建一个。同时,它也用于存储Vulkan程序相关状态的软件结构,因此,所有上层应用想要使能的验证层或特性扩展,都应该在创建Instance时被指定。

PhysicalDevice:表示GPU硬件的抽象。上层应用可以从Instance对象中枚举得到物理设备,之所以是枚举的原因在于可以存在多个物理设备。同时,可以从PhysicalDevice对象中查询VendorIdDeviceId和所有支持的特性,以及相关属性和限制。

PhysicalDevice对象可以枚举得到所有可用的Queue Families。最主要的就是图形队列,其次还有计算队列或传输队列。

PhysicalDevice对象还可以枚举得到支持Memory HeapsMemory TypesMemory Heap表示特定的RAM池,它能抽象母板上的RAM、或GPU片上的RAM、或任何其他Host-Or-Device内存。在分配内存时,上层应用必须指定Memory Type,它承载了堆内存的特定需求,比如:Host-VisibleCoherent(CPU and GPU Visible)。根据不同的供应商,这些类型会有不同的组合。

Device:它是基于物理设备创建的逻辑设备。它是Vulkan API中最基本的对象,几乎所有对象的创建都会依赖它。在创建逻辑设备时,需要指定期望使能的设备特性,比如:各向异性纹理过滤。同时,上层应用也必须指明将会使用的队列,包括其索引和Queue Families

Queue:它接收指令,并将指令提交到GPU上去执行。所有GPU执行任务,都会填充到CommandBuffers中,然后提交到Queues,使用Vulkan API函数vkQueueSubmit。如果,上层应用分别制定了图形队列和计算队列,则可以将不同的CommandBuffers提交对应的队列。

CommandPool:它是一个简单的对象,唯一的功能就是分配CommandBuffer。它也需要指定Queue Family

CommandBuffer:它是从CommandPool对象中分配的。它表示在逻辑设备上执行各种指令的缓冲。在这个指令缓冲上面,上层应用可以填充各种各样的指令,所有指令都有相同的前缀vkCmd

Sampler:它不会被绑定到任何特定的Image上。它更像是一组状态参数,比如:滤波模式(nearest or linear),或寻址模式(repeat, clamp-to-edge, clamp-to-border)。

BufferImage是两种占用设备内存的资源类型。

Buffer是相对简单的那种,它是任意的二进制数据的容器,且以字节(Byte)为单位的长度。

Image则表示像素集合。在其他API中,被称之为纹理(textture)。上层应用创建一个Image时,有很多参数需要指定。比如:类型上可以分为1D,2D,3D;像素格式也有很多种(比如:R8G8B8A8_UNORM or R32_SFLOAT);也可以是一组离散图像,用于mipmap;Image在不同的驱动实现下,可以有两种内部组成格式(tilinglayout)。

创建一个Buffer或者Image,驱动并不会自动为之分配内存。上层应用需要分成3步去创建:

  • Allocate DeviceMemory
  • Create Buffer or Image
  • Bing them together using function vkBindBufferMemory or vkBindImageMemory

这就是为什么上层应用必须创建DeviceMemory对象,它代表了一块内存,按照指定的内存类型和按照字节为单位指定大小。同时,上层应用不应该为每一个BufferImage分别创建一个DeviceMemory。取而代之的是,上层应用应该批发一大块DeviceMemory用于众多BufferImage。这是因为,分配DeviceMemory是一个开销较大的操作,同时,驱动也限制了DeviceMemory分配的数量。这个限制可以在PhysicalDevice中查询。

这里有一个例外,SwapChain中的Image则不需要上层应用主动分配并绑定DeviceMemory

BufferImage被创建、绑定DeviceMemory,在渲染过程中也不能被直接使用。

BufferView:它必须依赖已经创建好的Buffer对象,才能被创建。同时,创建的时候必须传递偏移和范围,来表示该BufferView仅仅只是访问其中一部分。

ImageView:它必须依赖已经创建好的Image对象,才能被创建。同时,也必须传递一组参数,来限制该ImageView的访问范围和方式。

着色器访问这些资源(BufferImageSampler)的方式是描述符。但是,描述符本身并不存在,它们总是被被描述符集管理。在创建描述符集之前,应用程序必须先创建一个DescriptorSetLayout,它是用于定义描述符集的行为模板。举个例子:假设,应用程序的某个着色器需要如下资源:

Binding slotResource
0One uniform buffer(called constant buffer in DirectX) available to the vertex shader stage.
1Another uniform buffer available to the fragment shader stage
2A sampled image
3A sampler, also available to the fragment shader stage

在创建描述符集之前,应用程序需要创建一个DescriptorPool,它专用于分配DescriptorSet。在创建DescriptorPool之前,应用程序必须指定将来需要使用的描述符集的类型以及最大可分配数量。

最终,应用程序分配DescriptorSet所需要的前置对象包括DescriptorPoolDescriptorSetLayout

DescriptorSet表示保存实际描述符集的内存,可以对其进行配置,以便描述符集指向特定的BufferBufferViewImageSampler。应用程序可以通过掉用函数vkUpdateDescriptorSets

可以通过函数vkCmdBindDescriptorSetsCommandBuffer中需要使用的描述符集绑定。这个函数还需要PipelineLayout对象。它表示渲染管线的配置,指定描述符集的类型将会在CommandBuffer中使用。

FrameBuffer:应用程序可以创建一个FrameBuffer对象,必须指定RenderPassImageView数组。同时,它的数量和格式必须匹配RenderPass

Pipeline:它是一个巨大的对象,因为以上几乎所有对象都会被它包含。它代表了整个管线的配置,拥有很多参数。其中之一就是PipelineLayout,用于定义描述符和推送常量。在Vulkan中,总共有两种管线,分别是ComputePipelineGraphicsPipelineComputePipeline相对而言比简单,仅仅用来实现计算程序,比如:Compute ShaderGraphicsPipeline比较复杂,它压缩了VertexFragmentGeometryComputeTessellation等过程,以及对应过程的所有参数属性,还有正反面剔除和混合模式。对于每一个不同参数集合的渲染过程,应用程序都需要为之创建一个新的管线。在渲染过程中,可以使用vkCmdBindPipeline函数来绑定当前渲染过程需要使用的管线。

Vulkan中,Shader编译是一个多阶段处理过程。首先,Vulkan不支持高级着色器语言,比如:GLSLHLSL,取而代之的是SPRI-V二进制;其次,SPRI-V二进制需要通过ShaderModule对象承载。

由于Pipeline是一个巨大的对象,每次创建都是高成本操作,为了加快它的创建,Vulkan引入一个新的对象PipelineCache,在创建Pipeline的过程中,应用程序可以选择的将PipelineCache对象传入其中,驱动程序将会根据此复用一些对象,以减少内存消耗。

Query:它用于从GPU中回读一些数据,主要包括:着色的像素数量、硬件耗时Counter等。

Semaphore:它的创建无需参数,用于Queue之间的同步。

Event:它的创建无需参数,专用于GPUCPU之间的同步,调用的函数是vkCmdSetEventvkCmdResetEventvkCmdWaitEvent。同样,也可以在CPU的其他线程中调用函数vkGetEventStatus获取Event的状态。

参考:https://gpuopen.com/learn/understanding-vulkan-objects/

参考:https://docs.vulkan.org/spec/latest/chapters/pipelines.html

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值