Skip to content

第四章 · 渲染管线 (Render Pipeline)

4.1 Render Pass 概述 🔥

4.1.1 什么是 Render Pass

Render Pass = 定义"如何渲染"的结构

类比:菜谱(定义食材、步骤、出锅方式)

一个 Render Pass 包含:
1. Attachments(附件): 图像/缓冲的定义
2. Subpasses(子通道): 渲染步骤
3. Dependencies(依赖): 子通道间的依赖关系

4.1.2 Render Pass 的结构

Render Pass
├── Attachments (附件定义)
│   ├── Attachment 0: Color (B8G8R8A8)
│   ├── Attachment 1: Depth (D32_SFLOAT)
│   └── Attachment 2: Stencil (S8_UINT)

├── Subpasses (子通道)
│   ├── Subpass 0 (主渲染)
│   │   ├── Input Attachments: []
│   │   ├── Color Attachments: [Attachment 0]
│   │   ├── Resolve Attachments: []
│   │   ├── Depth Attachment: [Attachment 1]
│   │   └── Stencil Attachment: []
│   └── ...

└── Dependencies (依赖)
    ├── Dependency 0: External → Subpass 0
    ├── Dependency 1: Subpass 0 → External
    └── ...

4.2 创建 Render Pass

4.2.1 完整的 Render Pass 创建

python
import vkbottle

# --- 1. 定义 Attachments ---
# Attachment = 一个图像的元数据
color_attachment = vkbottle.AttachmentDescription(
    format=swapchain_image_views[0].format,  # 交换链图像格式
    samples=vkbottle.SampleCountFlagBits._1,  # 单采样
    loadOp=vkbottle.AttachmentLoadOp.CLEAR,   # 加载操作:清除
    storeOp=vkbottle.AttachmentStoreOp.STORE, # 存储操作:存储
    stencilLoadOp=vkbottle.AttachmentLoadOp.DONT_CARE,
    stencilStoreOp=vkbottle.AttachmentStoreOp.DONT_CARE,
    initialLayout=vkbottle.ImageLayout.UNDEFINED,
    finalLayout=vkbottle.ImageLayout.PRESENT_SRC_KHR,  # 最终布局
)

depth_attachment = vkbottle.AttachmentDescription(
    format=VK_FORMAT_D32_SFLOAT,  # 深度格式
    samples=vkbottle.SampleCountFlagBits._1,
    loadOp=vkbottle.AttachmentLoadOp.CLEAR,
    storeOp=vkbottle.AttachmentStoreOp.DONT_CARE,
    stencilLoadOp=vkbottle.AttachmentLoadOp.DONT_CARE,
    stencilStoreOp=vkbottle.AttachmentStoreOp.DONT_CARE,
    initialLayout=vkbottle.ImageLayout.UNDEFINED,
    finalLayout=vkbottle.ImageLayout.DEPTH_STENCIL_READ_ONLY,
)

# --- 2. 定义 Subpasses ---
color_reference = vkbottle.AttachmentReference(
    attachment=0,  # Attachment 索引
    layout=vkbottle.ImageLayout.COLOR_ATTACHMENT_OPTIMAL,
)

depth_reference = vkbottle.AttachmentReference(
    attachment=1,  # Attachment 索引
    layout=vkbottle.ImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
)

subpass = vkbottle.SubpassDescription(
    pipelineBindPoint=vkbottle.PipelineBindPoint.GRAPHICS,
    colorAttachments=[color_reference],
    depthStencilAttachment=depth_reference,
)

# --- 3. 定义 Dependencies ---
# 确保颜色附件在渲染前就绪
dependency = vkbottle.SubpassDependency(
    srcSubpass=vkbottle.SUBPASS_EXTERNAL,  # 外部依赖
    dstSubpass=0,  # 当前子通道
    srcStageMask=vkbottle.AccessFlag.COLOR_ATTACHMENT_WRITE |
                 vkbottle.AccessFlag.COLOR_ATTACHMENT_READ,
    dstStageMask=vkbottle.AccessFlag.COLOR_ATTACHMENT_WRITE |
                 vkbottle.AccessFlag.COLOR_ATTACHMENT_READ,
    srcAccessMask=vkbottle.AccessFlag.COLOR_ATTACHMENT_WRITE |
                 vkbottle.AccessFlag.COLOR_ATTACHMENT_READ,
    dstAccessMask=vkbottle.AccessFlag.COLOR_ATTACHMENT_WRITE |
                 vkbottle.AccessFlag.COLOR_ATTACHMENT_READ,
    srcQueueFamilyIndex=vkbottle.QFO_TRANSFER_QUEUE_FAMILY_IGNORED,
    dstQueueFamilyIndex=vkbottle.QFO_TRANSFER_QUEUE_FAMILY_IGNORED,
)

# --- 4. 创建 Render Pass ---
render_pass_create_info = vkbottle.RenderPassCreateInfo(
    attachments=[color_attachment, depth_attachment],
    subpasses=[subpass],
    dependencies=[dependency],
)

render_pass = device.create_render_pass(render_pass_create_info)
print(f"Render Pass 已创建!")

4.2.2 Attachment 详解

属性说明
format像素格式
samples采样数(MSAA)
loadOp渲染前操作(CLEAR/LOAD/DONT_CARE)
storeOp渲染后操作(STORE/DONT_CARE/RESERVE)
stencilLoadOp模板加载操作
stencilStoreOp模板存储操作
initialLayout初始布局
finalLayout最终布局

4.2.3 Load/Store 操作选择

操作说明何时用
CLEAR清除为固定值每帧都需要
LOAD保留之前的内容需要上一帧的结果
DONT_CARE不关心(驱动可优化)不需要之前的内容

4.2.4 Layout 转换

Vulkan 中的图像有不同的"布局"(Layout),
不同布局有不同的性能特性:

UNDEFINED → 初始状态(什么都可以)
COLOR_ATTACHMENT_OPTIMAL → 颜色附件(写优化)
DEPTH_STENCIL_ATTACHMENT_OPTIMAL → 深度/模板附件
PRESENT_SRC_KHR → 呈现(用于 swapchain)
TRANSFER_SRC_OPTIMAL → 读取(如 copy)
TRANSFER_DST_OPTIMAL → 写入(如 copy)

布局转换由 Vulkan 自动处理(在 Render Pass 边界)
你只需要在 Attachments 中指定 initial/finalLayout

4.3 Graphics Pipeline 创建 🔥

4.3.1 Pipeline 整体结构

Graphics Pipeline = 所有渲染状态的编译结果
类比:编译好的程序

Pipeline 状态分为 7 个阶段:
1. Vertex Input(顶点输入)
2. Vertex Shader(顶点着色器)
3. Input Assembly(顶点组装)
4. Rasterization(光栅化)
5. Multisample(多重采样)
6. Color Blend(颜色混合)
7. Depth/Stencil Test(深度/模板测试)
8. Dynamic State(动态状态)

4.3.2 完整 Pipeline 创建

python
import vkbottle
import vkbottle.ext.glsl as glsl

# --- 1. 加载 Shader Modules ---
def load_shader_module(device, path, stage):
    with open(path, 'rb') as f:
        source = f.read()
    shader_create_info = vkbottle.ShaderModuleCreateInfo(pCode=source)
    return device.create_shader_module(shader_create_info), stage

vs_module, vs_stage = load_shader_module(device, 'vertex.spv', 
                                           vkbottle.ShaderStageFlag.VERTEX)
fs_module, fs_stage = load_shader_module(device, 'fragment.spv',
                                           vkbottle.ShaderStageFlag.FRAGMENT)

# --- 2. 定义 Shader Stages ---
shader_stage_info = [
    vkbottle.PipelineShaderStageCreateInfo(
        stage=vs_stage,
        module=vs_module,
        pName=b'main',  # 入口函数名
    ),
    vkbottle.PipelineShaderStageCreateInfo(
        stage=fs_stage,
        module=fs_module,
        pName=b'main',
    ),
]

# --- 3. 定义 Vertex Input ---
vertex_input_info = vkbottle.VertexInputStateCreateInfo(
    vertexBindingDescriptions=[  # 绑定描述
        vkbottle.VertexInputBindingDescription(
            binding=0,  # 绑定槽位
            stride=3 * 4,  # 步长(每个顶点 3x4 bytes = 12 bytes)
            inputRate=vkbottle.VertexInputRate.VERTEX,  # 每顶点
        ),
    ],
    vertexAttributeDescriptions=[  # 属性描述
        vkbottle.VertexInputAttributeDescription(
            location=0,  # Shader 中的 location
            binding=0,  # 绑定槽位
            format=vkbottle.Format.R32G32B32_SFLOAT,  # 格式(float32×3)
            offset=0,  # 偏移
        ),
        # 如果有颜色属性:
        # vkbottle.VertexInputAttributeDescription(
        #     location=1,
        #     binding=0,
        #     format=vkbottle.Format.R32G32B32A32_SFLOAT,
        #     offset=12,
        # ),
    ],
)

# --- 4. 定义 Input Assembly ---
input_assembly_info = vkbottle.InputAssemblyStateCreateInfo(
    topology=vkbottle.PrimitiveTopology.TRIANGLE_LIST,  # 图元类型
    primitiveRestartEnable=vkbottle.Bool(0),
)

# --- 5. 定义 Viewport + Scissor ---
viewport = vkbottle.Viewport(
    x=0.0,
    y=0.0,
    width=800.0,
    height=600.0,
    minDepth=0.0,
    maxDepth=1.0,
)

scissor = vkbottle.Rect2D(
    offset=(0, 0),
    extent=(800, 600),
)

viewport_state_info = vkbottle.ViewportStateCreateInfo(
    viewports=[viewport],
    scissors=[scissor],
)

# --- 6. 定义 Rasterization ---
rasterization_info = vkbottle.RasterizationStateCreateInfo(
    depthClampEnable=vkbottle.Bool(0),
    rasterizerDiscardEnable=vkbottle.Bool(0),
    polygonMode=vkbottle.PolygonMode.FILL,  # 填充模式
    cullMode=vkbottle.CullModeFlag.FRONT,   # 剔除模式
    frontFace=vkbottle.Face.FRONT_CCW,       # 正面(逆时针)
    depthBiasEnable=vkbottle.Bool(0),
    lineWidth=1.0,
)

# --- 7. 定义 Multisample ---
multisample_info = vkbottle.MultisampleStateCreateInfo(
    rasterizationSamples=vkbottle.SampleCountFlagBits._1,  # 单采样
    sampleShadingEnable=vkbottle.Bool(0),
    alphaToCoverageEnable=vkbottle.Bool(0),
)

# --- 8. 定义 Color Blend ---
blend_attachment = vkbottle.PipelineColorBlendAttachmentState(
    blendEnable=vkbottle.Bool(1),  # 混合启用(透明度)
    srcColorBlendFactor=vkbottle.BlendFactor.SRC_ALPHA,
    dstColorBlendFactor=vkbottle.BlendFactor.ONE_MINUS_SRC_ALPHA,
    colorBlendOp=vkbottle.BlendOp.ADD,
    srcAlphaBlendFactor=vkbottle.BlendFactor.ONE,
    dstAlphaBlendFactor=vkbottle.BlendFactor.ZERO,
    alphaBlendOp=vkbottle.BlendOp.ADD,
    colorWriteMask=vkbottle.ColorComponentFlag.R_BIT |
                    vkbottle.ColorComponentFlag.G_BIT |
                    vkbottle.ColorComponentFlag.B_BIT |
                    vkbottle.ColorComponentFlag.A_BIT,
)

color_blend_info = vkbottle.ColorBlendStateCreateInfo(
    logicOpEnable=vkbottle.Bool(0),
    logicOp=vkbottle.LogicOp.COPY,
    attachments=[blend_attachment],
    blendConstants=[0.0, 0.0, 0.0, 0.0],
)

# --- 9. 定义 Depth/Stencil Test ---
depth_stencil_info = vkbottle.DepthStencilStateCreateInfo(
    depthTestEnable=vkbottle.Bool(1),  # 深度测试启用
    depthWriteEnable=vkbottle.Bool(1),  # 深度写入启用
    depthCompareOp=vkbottle.CompareOp.LESS_OR_EQUAL,  # 深度比较(小于等于通过)
    depthBoundsTestEnable=vkbottle.Bool(0),
    stencilTestEnable=vkbottle.Bool(0),
    front=vkbottle.StencilOpState(),  # 默认
    back=vkbottle.StencilOpState(),
    minDepthBounds=0.0,
    maxDepthBounds=1.0,
)

# --- 10. 创建 Pipeline Layout ---
pipeline_layout = device.create_pipeline_layout(
    setLayouts=[descriptor_set_layout],  # 描述符集
    pushConstantRanges=[],  # 推送常量(可选)
)

# --- 11. 创建 Pipeline ---
graphics_pipeline_info = vkbottle.GraphicsPipelineCreateInfo(
    stageCreateInfos=shader_stage_info,
    vertexInputState=vertex_input_info,
    inputAssemblyState=input_assembly_info,
    viewportState=viewport_state_info,
    rasterizationState=rasterization_info,
    multisampleState=multisample_info,
    colorBlendState=color_blend_info,
    depthStencilState=depth_stencil_info,
    layout=pipeline_layout,
    renderPass=render_pass,
    subpass=0,
)

graphics_pipeline = device.create_graphics_pipelines(None, [graphics_pipeline_info])[0]
print(f"Pipeline 已创建! 状态数: {len(graphics_pipeline)}")

# --- 12. 清理 Shader Modules ---
vs_module.destroy()
fs_module.destroy()

4.4 图元拓扑类型

拓扑类型说明适用场景
TRIANGLE_LIST三角形列表(最常用 ✅)3D 模型
LINE_LIST线段列表网格线
LINE_STRIP线段链轮廓线
POINT_LIST点列表粒子系统
PATCH_LIST补丁列表(Tessellation)曲面

4.5 深度比较模式

模式说明
NEVER永不通过
LESS小于通过
EQUAL等于通过
LESS_OR_EQUAL小于等于通过(推荐 ✅)
GREATER大于通过
NOT_EQUAL不等于通过
GREATER_OR_EQUAL大于等于通过
ALWAYS总是通过

4.6 Pipeline 状态速查

状态关键参数
Vertex Inputbinding, stride, attribute
Input Assemblytopology(TRIANGLE_LIST)
Viewportx,y,width,height
Scissoroffset, extent
RasterizationpolygonMode, cullMode, frontFace
Multisamplesamples, alphaToCoverage
Color BlendblendEnable, src/dstFactor
Depth/StencildepthTestEnable, depthCompareOp
Pipeline LayoutdescriptorSetLayouts