《Vulkan Tutorial》

图形管线

图形管线是一系列将我们提交的顶点和纹理转换为渲染目标上的像素的操作。它的简化过程如下:

image-20250305204312379

  • input assembler 获取顶点数据
  • vertex shader 对每个顶点进行模型空间到屏幕空间的变换
  • tessellation shader 根据一定的规则对几何图形进行细分,从而提高网格质量
  • geometry shader 可以以图元为单位处理几何图形,它可以剔除图元、输出图元,与 tessellation shader 相似,但现在已经不推荐使用
  • rasterization 阶段将图元分成片元,这一阶段会执行深度测试
  • fragment shader 对每一个未丢弃的片元进行处理,确定哪些片元写入帧缓冲
  • color blending 阶段对写入帧缓冲的同一像素位置的不同片段进行混合

绿色标识的是固定功能阶段,固定功能阶段允许通过参数对处理过程进行一定程度的配置

橙色标识的是可编程阶段,允许将自己的代码加载到显卡

相关概念

  • Instance

  • PhysicalDevice、Device

  • QueueFamily、Queue

  • Surface、Swapchain

  • CommandPool、CommandBuffer

  • Semaphore、Fence:

    • Semaphore 用于 GPU-GPU 同步
    • Fence 用于 CPU-GPU 同步,有 waitForFences 函数
  • Image、ImageView

  • Shader

  • Pipeline、ShaderModule

简要流程

前置工作:

  • Create Window:调用 GLFW 相关函数

  • Create Instance:需要填入需要的 Layers、Extensions,得到 vk::UniqueInstance

  • Create Surface:调用 GLFW 相关函数,得到 vk::UniqueSurfaceKHR

  • Select GPU:选择一个合适的 vk::PhysicalDevicevk::QueueFamily,可能要求它必须可以 Present、支持交换链等

  • Create Device:根据选择的 vk::PhysicalDevicevk::QueueFamily 创建 vk::UniqueDevice 和一些 vk::Queue

  • Create Swapchain:

    • 根据 vk::UniqueDevic 和窗口的 frame buffer 大小创建一个 vk::UniqueSwapchainKHR
    • vk::UniqueSwapchainKHR 中获取足够数量(3 个)的 vk::Image
    • 每个 vk::Image 创建一个 vk::UniqueImageView
    • 每个 vk::Image 创建一个 vk::Semaphore,用来同步“可以呈现”
  • Create Render Sync:

    • 创建 1 个 vk::UniqueCommandPool
    • 用 Command Pool 创建 3 个 vk::CommandBuffer,用于写入命令,每个 vk::UniqueImageView 对应一个
    • 每个 Command Buffer 创建 1 个 vk::UniqueSemaphore,用来同步“可以执行渲染指令”
    • 每个 Command Buffer 创建 1 个 vk::UniqueFence,用来同步“渲染指令已经执行完成”,初始设置为 signaled
  • Create Shader:读取 spirv 二进制文件,创建 vertex 和 fragment 两个 vk::UniqueShaderEXT

主循环:

  • Acquire Render Target:
    • 等待当前帧对应的 vk::CommandBuffer 的“渲染指令已经执行完成” Fence 信号(阻塞)
    • 获取下一个 vk::Image 和对应的 vk::ImageView,当它准备完成时(即不会阻塞 CPU,而是异步等待),会将当前帧对应的 vk::CommandBuffer 的“可以执行渲染指令” Semaphore 信号置为 signaled
    • 如果 Vulkan 表示需要重建 Swapchain,那么就重建,跳过下面的步骤
    • 重置“渲染指令已经执行完成” Fence 信号
  • Begin Frame:开启当前帧对应 vk::CommandBuffer 的录制
    • Transition For Render:创建 vk::ImageMemoryBarrier2 来转换对应 vk::Image 的 Layout(Undefined 到 ColorAttachmentOptimal)
    • Render:
      • 渲染指令开始
      • 将创建的 vk::UniqueShaderEXT 绑定到 vk::CommandBuffer
      • 渲染
      • 渲染指令结束
    • Transition For Present:转换 Layout(ColorAttachmentOptimal 到 PresentSrcKHR)
  • Submit And Present:
    • 结束录制
    • vk::CommandBuffer 提交给 vk::CommandQueue(异步):
      • 提交
      • 执行
      • 当执行到“写入颜色缓冲区”这一步时,等待当前 vk::CommandBuffer 的“可以执行渲染指令” Semaphore 信号
      • 完成“写入颜色缓冲区”这一步后,触发当前 vk::Image 的“可以呈现” Semaphore 信号
      • 继续执行
      • 全部执行完成后,触发当前 vk::CommandBuffer 的“渲染指令已经执行完成” Fence 信号
    • 呈现(异步):
      • 等待当前 vk::Image 的“可以呈现” Semaphore 信号
      • 呈现
      • 如果错误,那么可能需要重建 Swapchain,此时进行重建