Files
XCEngine/docs/api/XCEngine/RHI/Vulkan/VulkanPipelineState/VulkanPipelineState.md
2026-03-29 01:36:53 +08:00

6.5 KiB
Raw Blame History

VulkanPipelineState

命名空间: XCEngine::RHI

类型: class

头文件: XCEngine/RHI/Vulkan/VulkanPipelineState.h

描述: Vulkan 后端里的 RHIPipelineState 实现,负责创建 graphics / compute pipeline并保存与之相关的 layout、render target 格式和固定功能状态。

概览

VulkanPipelineState 是当前 Vulkan 后端里 draw / dispatch 能否真正执行的关键对象。

它承担两类职责:

  • 保存抽象层的 pipeline 描述,例如输入布局、光栅化、混合、深度模板、拓扑和 render target 格式
  • 在合适时机创建原生 VkPipeline

但它不是一个完全成熟、完全统一的 PSO 系统。当前 graphics 和 compute 路径的成熟度并不一致,这一点文档必须明确写出来。

生命周期

常见用法有两种:

图形管线

  1. 构造 VulkanPipelineState
  2. Initialize(VulkanDevice*, const GraphicsPipelineDesc&)
  3. 录制阶段把它传给 VulkanCommandListSetPipelineState()
  4. 不再使用时 Shutdown()

计算管线

  1. 初始化 pipeline state让它至少拥有 pipeline layout
  2. SetComputeShader(RHIShader*)
  3. 在录制 Dispatch() 前由 EnsureValid() 延迟创建 compute pipeline

当前实现的真实行为

graphics 与 compute 的差异

按当前实现:

  • Initialize() 面向的是 GraphicsPipelineDesc
  • 如果 vertexShaderfragmentShader 都没有有效 payloadInitialize() 仍可能把对象标记为 m_isConfigured = true
  • graphics 路径要求:
    • renderTargetCount == 1
    • 第一项 render target format 有效
    • vertex shader 和 fragment shader 都存在
  • compute 路径不是在 Initialize() 里直接创建,而是走 SetComputeShader() + EnsureValid() + CreateComputePipeline()

因此当前类名虽然叫 PipelineState,但本质上是“图形优先、计算补充”的实现。

Pipeline Layout

EnsurePipelineLayout(desc) 有两种行为:

  • 如果 desc.pipelineLayout 非空,直接借用外部 VulkanPipelineLayout
  • 否则创建一个空的 VkPipelineLayout

这解释了为什么 compute 路径能先有 pipeline layout再延迟绑定 compute shader。

Graphics Pipeline 创建

CreateGraphicsPipeline() 当前会:

  • CompileVulkanShader(...)desc.vertexShader / desc.fragmentShader 编译成 SPIR-V
  • 临时创建两个 VkShaderModule
  • 根据 render target / depth format 构造一个内部 VkRenderPass
  • 再据此创建 graphics pipeline

需要注意的是,当前这个 graphics pipeline 会自己生成一个内部 VkRenderPass。这和一些成熟引擎里把 render pass / framebuffer compatibility 单独抽出来统一管理的做法相比,还处于更简单直接的阶段。

Compute Pipeline 创建

CreateComputePipeline() 当前要求:

  • m_device 有效
  • m_pipelineLayout 有效
  • m_computeShader 非空
  • m_computeShader 实际上是有效的 VulkanShader

创建时会从 VulkanShader 里拿 VkShaderModule 与 entry point然后调用 vkCreateComputePipelines()

IsValid() 与真实 native pipeline

这是当前实现里最容易误解的一点:

  • IsValid() 返回的是 m_isConfigured
  • 它不等价于“m_pipeline != VK_NULL_HANDLE 一定成立”

尤其在 compute 路径里pipeline 可能要等 EnsureValid() 之后才真正创建。因此把 IsValid() 理解成“配置状态可用”更准确,而不是“原生对象已经存在”。

其他行为

  • Bind() / Unbind() 当前都是 no-op
  • GetHash() 目前只用了拓扑和第一个 render target format 做轻量 hash
  • HasDepthStencilAttachment() 只按 m_depthStencilFormat 是否为 Unknown 判断

线程语义

当前实现没有内部锁。更安全的工程约束是:

  • pipeline state 构建与修改在单线程完成
  • 构建完成后再交给录制线程使用
  • 不要在录制过程中并发修改同一个 pipeline state

所有权与资源管理

  • VulkanPipelineState 持有 VkPipeline
  • 可能持有自建的 VkPipelineLayout
  • graphics 路径还会持有内部创建的 VkRenderPass
  • 如果借用了外部 VulkanPipelineLayout,则不会在自身 Shutdown() 中销毁它

设计取向

当前实现非常典型地体现了“先让 pipeline 概念在跨后端 RHI 里工作起来,再逐步收敛成更严谨的 Vulkan 语义”:

  • 优点是 graphics / compute 基本链路已经能跑
  • 代价是状态有效性、render pass 归属和 hash 策略仍较简化

这类设计在商业引擎重构早期很常见。真正成熟后,通常会继续把 pipeline cache、render pass 兼容性、shader reflection 和 descriptor layout 规则再往前推一层。

当前限制

  • graphics 路径当前只支持一个 render target
  • compute pipeline 为延迟创建模型
  • IsValid() 不严格等价于 native pipeline 已创建
  • Bind() / Unbind() 无实际行为
  • GetHash() 很简化,不能视为完整 pipeline cache key

主要公开方法

  • bool Initialize(VulkanDevice* device, const GraphicsPipelineDesc& desc)
  • void SetInputLayout(const InputLayoutDesc& layout)
  • void SetRasterizerState(const RasterizerDesc& state)
  • void SetBlendState(const BlendDesc& state)
  • void SetDepthStencilState(const DepthStencilStateDesc& state)
  • void SetTopology(uint32_t topologyType)
  • void SetRenderTargetFormats(uint32_t count, const uint32_t* formats, uint32_t depthFormat)
  • void SetSampleCount(uint32_t count)
  • void SetComputeShader(RHIShader* shader)
  • PipelineStateHash GetHash() const
  • RHIShader* GetComputeShader() const
  • bool HasComputeShader() const
  • bool IsValid() const
  • void EnsureValid()
  • void Shutdown()
  • void Bind()
  • void Unbind()
  • void* GetNativeHandle()
  • PipelineType GetType() const

相关测试与使用线索

  • tests/RHI/unit/test_vulkan_graphics.cpp 覆盖了 Vulkan graphics pipeline 与 compute dispatch 的真实创建路径
  • tests/RHI/unit/test_compute.cpp 会把 compute shader、pipeline layout 和 descriptor set 接起来验证计算路径
  • tests/RHI/unit/test_command_list.cpp 会通过抽象命令列表接口驱动 pipeline 绑定与 dispatch

相关文档