问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Vulkan入门:从基础概念到渲染流程详解

创作时间:
作者:
@小白创作中心

Vulkan入门:从基础概念到渲染流程详解

引用
1
来源
1.
https://www.cnblogs.com/ArsenalfanInECNU/p/18096919

Vulkan是新一代的图形API,相比传统的OpenGL和Direct3D,Vulkan提供了更底层的硬件访问能力,同时也带来了更高的开发难度。本文将从Vulkan的基本概念入手,详细介绍Vulkan的核心对象和使用方法,并分享一些学习和开发Vulkan的经验。

基本概念

要理解Vulkan,最重要的是理清Vulkan的各种对象的作用及其关联性。这些对象与实际使用的上层概念有一定距离,且关系较为复杂,只有深入理解这些基础概念,才能顺畅地使用Vulkan。

Queue

队列(Queue)是从Mantle世代API引入的概念,可以看作是对硬件执行流水线的抽象。通过提交任务来执行图形计算。简单来说,提交的内容在一个队列上只能按顺序执行。GPU的并行执行并不是像多线程那样可以随意fire多个task,而是按照一定的规则排列。

  • QueueFamily与Queue:QueueFamily(队列家族)包含若干个Queue。常见的QueueFamily有三种:Graphic、Compute和Transfer。Graphic队列通常功能最全面,包含的Queue数量也最多;Compute队列不支持Graphic相关功能;Transfer队列专门用于数据上传。这种分类不是绝对的,具体取决于硬件提供的QueueFamily数量和支持的功能。Vulkan中单个Queue禁止同一时间被多个线程操作,因此申请多个Queue可以实现多线程Submit。

Command

录制使用的对象是Command Pool和Command Buffer。录制过程类似于push_back操作,每次调用vkCmd开头的函数都会向Command Buffer中添加内容。Command Buffer在CPU还是GPU上取决于硬件和驱动实现,对Vulkan程序员来说是透明的。录制完成后,可以将Command Buffer提交给Queue执行。

Buffer

Vulkan主要有两种基本对象:Buffer和Image。Buffer用于存储顶点、索引和Uniform数据,而Image用于存储位图数据,即贴图。Buffer要真正使用还需要配备一个VkDeviceMemory。

Mesh

Mesh由Vertex Buffer和Index Buffer组成,每个Buffer都需要一个Memory对象。Vulkan教程中对这部分的解释已经很清晰。

Texture

VkImage除了需要VkDeviceMemory,还需要VkImageView和VkImageSampler。VkImageView相当于一个访问器,VkImage和VkImageView通常绑定在一起使用。VkImageSampler用于设置各向异性过滤、Mipmaps等采样参数,多个图像可以共享一个采样器。

Uniform

Uniform类似于Mesh,但需要特别注意内存对齐问题。不同编译器和平台生成的内存对齐模式可能不同,而Vulkan对传入的数据有对齐要求。这个问题在教程中有所提及,但容易被忽视。

Memory

在Vulkan中,GPU内存的管理交给了程序员。VkDeviceMemory对象代表分配的显存。这类似于C/C++中的手动内存管理,允许开发者实现更高级的分配策略。Vulkan不鼓励小分配,每个设备的分配次数都有上限,因此推荐使用子分配(suballocation)。可以使用现成的Vulkan Memory Allocator来简化内存管理。

Synchronization

Vulkan要求开发者手动处理同步问题:

  • Fence:用于CPU和GPU之间的同步,CPU可以通过等待Fence来知道GPU是否完成任务。
  • Semaphore:用于两个提交之间建立依赖关系,避免低效的Fence等待。
  • Barrier:用于处理缓存一致性问题,确保数据在不同阶段正确同步。
  • Event:使用较少。
  • Subpass Dependency:提供类似Barrier的功能,专门用于Subpass。

Pipeline

VkPipeline定义了管线状态,包括隐藏面剔除、混合等设置,最重要的是在这里绑定Shader。一旦Pipeline创建完成,就不能修改,需要更换Shader时必须创建新的Pipeline。Compute Shader需要独立的Compute Pipeline。Pipeline Cache用于加速Shader加载,因为Shader代码需要编译为GPU专用的最优代码。

FrameBuffer

FrameBuffer类似于OpenGL中的Attachment,通过引用各种ImageView打包成一个集合。从SwapChain获得的Image创建ImageView后,就可以用于GPU渲染。

RenderPass

RenderPass是Vulkan中最复杂的概念之一。它描述了一次渲染所需的绘制目标的配置。创建RenderPass时需要指定Attachment的描述,如颜色附件和深度附件。RenderPass通过FrameBuffer绑定实际的Image。Vulkan要求在创建RenderPass时绑定一个FrameBuffer,以确保兼容性。

Descriptor

Shader中读取数据主要通过Uniform实现。Uniform数据通过DescriptorSet或PushConstant传输:

  • DescriptorSet:引用用于作为Uniform的Buffer数据,包括VkBuffer和VkImage。DescriptorSet需要从DescriptorPool分配,并通过vkUpdateDescriptorSets()方法绑定具体对象。
  • DescriptorSetLayout:规范约束DescriptorSet的结构,定义每个Set中包含的Buffer和Image数量。
  • PipelineLayout:结合DescriptorSetLayout和PushConstant,用于创建Pipeline时指定Shader的数据布局。

渲染流程

Vulkan的渲染流程主要包括以下步骤:

  1. vkAcquireNextImageKHR:从SwapChain获取下一个可绘制到屏幕的Image。
  2. vkResetCommandPool/vkResetCommandBuffer:清除上一次录制的CommandBuffer。
  3. vkBeginCommandBuffer:开始录制CommandBuffer。
  4. vkCmdBeginRenderPass:启用RenderPass并绑定FrameBuffer。
  5. vkCmdBindPipeline:绑定Pipeline,更换Shader需要重新绑定。
  6. vkCmdBindDescriptorSets:绑定DescriptorSets,需要给出PipelineLayout。
  7. vkCmdBindVertexBuffers/vkCmdBindIndexBuffer:绑定模型数据。
  8. vkCmdDrawIndexed:执行绘制命令。
  9. vkCmdEndRenderPass:结束RenderPass。
  10. vkEndCommandBuffer:结束CommandBuffer录制。
  11. vkQueueSubmit:提交渲染任务。
  12. vkQueuePresentKHR:呈现渲染数据。

学习资源推荐

  • Khronos Group Vulkan Samples:官方示例代码仓库,包含大量Vulkan应用实例。
  • Sascha Willems Vulkan Samples:另一个知名的Vulkan示例代码仓库。
  • NVIDIA DesignWorks Samples:NVIDIA的Vulkan示例代码仓库。
  • Writing an efficient Vulkan renderer:关于Vulkan最佳实践的文章,提供了许多可行方案。
  • 2017 DevU - 04 - Synchronization:关于Vulkan同步机制的视频教程。
  • RenderDoc:用于Vulkan应用调试的工具,可以帮助定位渲染问题。

学习建议

  • 先学习上层渲染框架:建议先使用Unity等上层渲染框架实现一套完整的渲染管线,这样可以更好地理解Vulkan的底层细节。
  • 关注性能优化:Vulkan开发中性能优化是最重要的,需要在数据结构和多线程开发等方面进行深入研究。
  • 利用官方资源:Vulkan的官方规格文档是最权威的参考资料,遇到问题可以到Khronos Group的官方论坛寻求帮助。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号