32 KiB
32 KiB
Custom Shader Widget 实例化渲染 API 设计文档
版本: 1.1 日期: 2025-01 状态: 设计草案 关联问题: P2-003 (Custom Shader Widget 无法合批)
目录
1. 设计目标与约束
1.1 设计目标
| 目标 | 描述 | 优先级 |
|---|---|---|
| 用户侧 API 简洁性 | 用户创建控件的方式应与现有方式相似或更简单 | 高 |
| 自动实例化合批 | 相同着色器的控件自动识别并合批,用户无感知 | 高 |
| 性能提升 | 100 个相同类型控件 → 1 个 Draw Call | 高 |
| 向后兼容 | 不破坏现有用户代码,渐进式采用 | 中 |
| 类型安全 | 编译期验证实例数据布局 | 中 |
1.2 设计约束
| 约束 | 说明 | 影响 |
|---|---|---|
| Push Constants 128 字节限制 | Vulkan 最小保证 128 字节 | 实例数据需要通过 Storage Buffer 传递 |
| 着色器兼容性 | 需要使用 gl_InstanceIndex |
需要特殊的实例化顶点着色器 |
| 描述符绑定 | 合批控件可能有不同的纹理绑定 | 需要设计纹理数组或分组策略 |
| 裁剪区域差异 | 不同控件可能有不同的 clip_rect | 需要按裁剪区域分组 |
1.3 合批粒度决策
决策:按 shader_id + clip_rect 分组合批
合批分组键 = hash(shader_id, clip_rect_hash, render_mode)
理由:
- 相同着色器的控件共享 Pipeline
- 不同裁剪区域必须分开绘制(Vulkan 限制)
- 渲染模式决定了 Pipeline 配置
2. API 设计方案
2.1 用户侧 API(零改动方案)
核心设计理念:用户完全不需要改变现有代码,实例化合批完全透明。
// ============================================================================
// 用户代码示例 - 与现有方式完全相同
// ============================================================================
// 1. xxx_shader_spec 由工具自动生成(用户不需要手写)
// 生成位置:build/generated/shaders/gradient_vert_frag.h
//
// 自动生成的内容包括:
// - gradient_shader_spec<ParamsT>
// ├── params_type (GradientParams,UBO 参数)
// ├── push_constant_type (GradientPushConstantCustom)
// ├── get_fragment_spirv()
// ├── get_bindings()
// └── shader_id
// 2. 用户只需包含生成的头文件,创建控件类
#include "generated/shaders/gradient_vert_frag.h"
using namespace mirage::shaders;
class my_gradient : public procedural_shader_widget<gradient_shader_spec<>> {
public:
/// @brief 设置渐变颜色
auto& set_colors(const color& start, const color& end) {
// push_constants() 访问 GradientPushConstantCustom(每实例数据)
push_constants().start_color = start;
// params() 访问 GradientParams(共享 UBO 参数)
params().end_color = end;
mark_params_dirty();
push_constant_dirty_ = true;
return *this;
}
/// @brief 设置渐变类型
auto& set_gradient_type(float type) {
params().gradient_type = type;
mark_params_dirty();
return *this;
}
};
// 3. 使用控件(与现有方式完全相同)
void build_ui() {
for (int i = 0; i < 100; ++i) {
auto gradient = std::make_shared<my_gradient>();
gradient->set_colors(color::red(), color::blue());
gradient->set_size({100, 100});
gradient->set_position({i * 110.0f, 0.0f});
parent->add_child(gradient);
}
// 框架自动将这 100 个控件合批为 1 个 Draw Call!
}
着色器源文件示例:
// shaders/widget/gradient.frag.glsl
// 工具会自动解析此文件并生成 gradient_shader_spec
layout(push_constant) uniform PushConstants {
vec2 scale; // Header 部分(系统填充)
vec2 translate;
// Custom 部分 → 生成 GradientPushConstantCustom
vec4 start_color; // 每实例的起始颜色
} pc;
layout(set = 0, binding = 0) uniform GradientParams {
vec4 end_color; // 共享的结束颜色
float gradient_type; // 渐变类型
float gradient_angle; // 渐变角度
vec2 _padding;
};
2.2 实例化数据自动收集机制
框架通过以下机制自动收集实例数据:
// ============================================================================
// 框架内部:实例数据收集器
// ============================================================================
/// @brief 实例化数据收集器(框架内部使用)
class instanced_batch_collector {
public:
/// @brief 添加控件到批次
/// @param cmd 渲染命令
/// @return 是否成功添加到现有批次
bool try_add_to_batch(const custom_shader_widget_command& cmd);
/// @brief 完成收集,生成实例化命令
auto finalize() -> std::vector<instanced_custom_shader_command>;
private:
/// 批次键
struct batch_key {
uint32_t shader_id;
size_t clip_rect_hash;
custom_shader_render_mode render_mode;
bool operator==(const batch_key&) const = default;
};
struct batch_key_hash {
size_t operator()(const batch_key& k) const noexcept;
};
/// 待处理批次
std::unordered_map<batch_key, std::vector<custom_shader_widget_command>, batch_key_hash> batches_;
};
2.3 可选的显式实例化 API(高级用户)
对于需要更细粒度控制的高级用户,提供显式 API:
// ============================================================================
// 高级 API:显式实例化控件
// ============================================================================
/// @brief 实例化着色器控件(高级 API)
/// @tparam ShaderSpec 着色器规格
/// @tparam InstanceData 实例数据类型(必须符合 instance_data_concept)
template <typename ShaderSpec, typename InstanceData = default_instance_data>
requires procedural_shader_spec_concept<ShaderSpec> &&
instance_data_concept<InstanceData>
class instanced_shader_widget : public leaf_widget_base {
public:
/// @brief 添加实例
/// @param data 实例数据
/// @return 实例 ID
uint32_t add_instance(const InstanceData& data);
/// @brief 更新实例
/// @param id 实例 ID
/// @param data 新数据
void update_instance(uint32_t id, const InstanceData& data);
/// @brief 移除实例
/// @param id 实例 ID
void remove_instance(uint32_t id);
/// @brief 清空所有实例
void clear_instances();
/// @brief 获取实例数量
[[nodiscard]] size_t instance_count() const noexcept;
protected:
std::vector<InstanceData> instances_;
bool instances_dirty_ = true;
};
// 使用示例
struct my_instance_data {
vec4f_t rect; // x, y, width, height
vec4f_t color; // RGBA
};
class particle_system : public instanced_shader_widget<particle_spec, my_instance_data> {
public:
void spawn_particle(const vec2f_t& pos, const color& col) {
add_instance({
.rect = {pos.x(), pos.y(), 10.0f, 10.0f},
.color = col
});
}
};
2.4 实例数据概念定义
// ============================================================================
// 实例数据 Concept
// ============================================================================
/// @brief 默认实例数据(用于自动合批)
///
/// 支持 2D 变换:位置、大小、旋转、锚点
/// 总大小:64 字节(4 x vec4)
struct default_instance_data {
vec4f_t rect; ///< 位置和大小: x, y, width, height
vec4f_t transform; ///< 变换参数: rotation(弧度), anchor_x, anchor_y, scale
vec4f_t custom_data_0; ///< 用户自定义数据槽 0
vec4f_t custom_data_1; ///< 用户自定义数据槽 1
};
/// @brief 变换参数说明
/// - transform.x (rotation): 旋转角度(弧度),绕锚点旋转
/// - transform.y (anchor_x): 锚点 X(0.0 = 左, 0.5 = 中, 1.0 = 右)
/// - transform.z (anchor_y): 锚点 Y(0.0 = 上, 0.5 = 中, 1.0 = 下)
/// - transform.w (scale): 统一缩放因子(1.0 = 原始大小)
/// @brief 实例数据 Concept
template <typename T>
concept instance_data_concept = requires {
// 必须是标准布局类型
requires std::is_standard_layout_v<T>;
// 对齐不超过 16 字节
requires alignof(T) <= 16;
// 大小是 16 字节的倍数(vec4 对齐)
requires sizeof(T) % 16 == 0;
// 大小不超过 256 字节(合理限制)
requires sizeof(T) <= 256;
};
static_assert(instance_data_concept<default_instance_data>);
3. 代码生成工具集成
3.1 现有代码生成流程
当前 xxx_shader_spec 由 tools/spirv_parser.py 从着色器 SPIR-V 自动生成:
着色器源文件 (.glsl)
↓ glslc 编译
SPIR-V 二进制 (.spv)
↓ spirv_parser.py 解析
生成的头文件 (xxx_vert_frag.h)
└── xxx_shader_spec<ParamsT>
├── params_type (UBO 参数)
├── push_constant_type (Push Constants Custom 部分)
├── get_fragment_spirv()
├── get_bindings()
└── shader_id
3.2 实例化着色器生成方案
代码生成工具检测着色器是否使用 gl_InstanceIndex,自动生成实例化版本:
# tools/spirv_parser.py 扩展
def detect_instancing_support(spirv_data: bytes) -> bool:
"""检测着色器是否使用 gl_InstanceIndex"""
# 检查 OpDecorate BuiltIn InstanceIndex
...
def generate_shader_spec(spirv_data: bytes, shader_name: str) -> str:
"""生成着色器规格代码"""
uses_instancing = detect_instancing_support(spirv_data)
if uses_instancing:
# 生成支持实例化的规格
return generate_instanced_shader_spec(...)
else:
# 生成标准规格
return generate_standard_shader_spec(...)
检测逻辑:
- 解析顶点着色器 SPIR-V
- 查找
OpDecorate %id BuiltIn InstanceIndex指令 - 如果存在,标记该着色器支持实例化
- 生成相应的
shader_spec,添加supports_instancing = true标记
3.3 实例数据结构生成
代码生成工具根据着色器的 Push Constants Custom 部分自动生成实例数据结构:
// 从着色器 Push Constants 自动提取
// 原始 BlurPushConstantCustom:
// float intensity; // 4 bytes
// vec2 viewport_size; // 8 bytes
// 生成的实例数据结构(添加 rect 字段)
struct blur_instance_data {
vec4f_t rect; // x, y, width, height(必须,用于实例定位)
float intensity; // 从 push_constant_type 提取
vec2f_t viewport_size; // 从 push_constant_type 提取
float _padding; // 对齐到 16 字节边界
};
生成规则:
- 强制字段:
vec4f_t rect始终作为第一个字段 - 复制字段:从
push_constant_type复制所有成员 - 对齐填充:确保总大小是 16 字节的倍数
3.4 生成代码示例
// ============ 自动生成的实例化支持 ============
/// @brief blur 实例数据结构(自动生成)
struct alignas(16) blur_instance_data {
// 必须字段:实例位置和大小
Eigen::Vector4f rect; // x, y, width, height
// 从 push_constant_type 提取的字段
float intensity;
alignas(8) Eigen::Vector2f viewport_size;
// 填充到 48 字节(16 的倍数)
float _padding[1];
};
static_assert(sizeof(blur_instance_data) % 16 == 0,
"Instance data must be 16-byte aligned");
/// @brief blur 实例化着色器规格(自动生成)
template <typename ParamsT = BlurParams>
struct blur_instanced_shader_spec {
using params_type = ParamsT;
using instance_type = blur_instance_data;
static constexpr bool supports_instancing = true;
static constexpr uint32_t shader_id = blur_shader_spec<ParamsT>::shader_id;
[[nodiscard]] static constexpr auto get_instance_spirv() noexcept
-> std::span<const uint32_t> {
return blur_instanced_vert_spirv; // 实例化顶点着色器
}
[[nodiscard]] static constexpr auto get_fragment_spirv() noexcept
-> std::span<const uint32_t> {
return blur_frag_spirv; // 复用片段着色器
}
// 描述符绑定(添加实例 SSBO)
[[nodiscard]] static constexpr auto get_bindings() noexcept
-> std::span<const shader_binding_info> {
return blur_instanced_bindings;
}
};
3.5 着色器编写约定
为支持实例化,着色器作者需要遵循以下约定:
标准着色器(不变)
// blur.vert.glsl - 标准版本(保持不变)
layout(push_constant) uniform PushConstants {
vec2 scale;
vec2 translate;
// Custom 部分
float intensity;
vec2 viewport_size;
} pc;
void main() {
// 使用 pc.xxx 访问参数
}
实例化着色器(新增)
// blur_instanced.vert.glsl - 实例化版本
// 命名约定:原名_instanced.vert.glsl
layout(push_constant) uniform PushConstants {
vec2 scale; // 所有实例共享
vec2 translate; // 所有实例共享
} pc;
// 实例数据 SSBO
struct InstanceData {
vec4 rect; // x, y, width, height
float intensity; // 每实例参数
vec2 viewport_size; // 每实例参数
float _padding;
};
layout(std430, set = 0, binding = 0) readonly buffer InstanceBuffer {
InstanceData instances[];
};
void main() {
InstanceData inst = instances[gl_InstanceIndex];
// 使用 inst.xxx 访问实例参数
}
3.6 代码生成工具修改清单
| 文件 | 修改内容 |
|---|---|
tools/spirv_parser.py |
添加 gl_InstanceIndex 检测 |
tools/type_mapping.py |
添加实例数据类型映射 |
tools/code_generator.py |
生成 instance_type 和 instanced_shader_spec |
cmake/compile_shaders.cmake |
支持 *_instanced.vert.glsl 编译 |
4. 数据流架构
4.1 整体数据流
flowchart TB
subgraph 用户代码
W1[Widget 1]
W2[Widget 2]
W3[Widget N]
end
subgraph render_collector
RC[收集渲染命令]
end
subgraph render_tree_builder
RTB[构建渲染树]
IBC[实例批次收集器]
end
subgraph render_tree_executor
RTE[执行渲染]
end
subgraph custom_shader_widget_renderer
CSWR[实例化渲染]
SSBO[Instance SSBO]
end
W1 --> RC
W2 --> RC
W3 --> RC
RC --> RTB
RTB --> IBC
IBC --> |分组合批| RTE
RTE --> CSWR
CSWR --> SSBO
SSBO --> |gl_InstanceIndex| GPU
4.2 合批流程详解
sequenceDiagram
participant User as 用户控件
participant Collector as render_collector
participant Builder as render_tree_builder
participant Batcher as instanced_batch_collector
participant Executor as render_tree_executor
participant Renderer as custom_shader_widget_renderer
User->>Collector: build_render_command
Note over Collector: 收集单个命令
Collector->>Builder: 传递命令列表
Builder->>Batcher: process_custom_shader_widget
Note over Batcher: 按 shader_id + clip_rect 分组
Builder->>Builder: 生成 instanced_node
Builder->>Executor: 渲染树
Executor->>Renderer: render_instanced
Note over Renderer: 1. 上传实例数据到 SSBO
Note over Renderer: 2. 绑定 Pipeline
Note over Renderer: 3. vkCmdDraw with instanceCount
4.3 GPU 数据布局
+------------------+ +----------------------+ +------------------+
| Push Constants | | Instance SSBO | | UBO (共享参数) |
| (128 bytes) | | (动态大小) | | (每批次一个) |
+------------------+ +----------------------+ +------------------+
| scale (vec2) | | instance[0] | | 着色器特定参数 |
| translate (vec2) | | rect (vec4) | | (如 end_color) |
| 保留空间 | | transform (vec4) | +------------------+
+------------------+ | custom_0 (vec4) |
| custom_1 (vec4) |
| instance[1] |
| ... |
| instance[N-1] |
+----------------------+
transform 布局:
.x = rotation (弧度)
.y = anchor_x (锚点 X, 0-1)
.z = anchor_y (锚点 Y, 0-1)
.w = scale (缩放因子)
5. 关键数据结构
5.1 实例化渲染命令
// ============================================================================
// 新增:实例化渲染命令
// ============================================================================
/// @brief 实例化自定义着色器渲染命令
struct instanced_custom_shader_command {
// ========================================================================
// 基本信息
// ========================================================================
uint32_t shader_id; ///< 着色器唯一标识
render_order order; ///< 渲染顺序(使用第一个实例的 z_order)
// ========================================================================
// 着色器配置(所有实例共享)
// ========================================================================
std::span<const uint32_t> vert_spirv; ///< 顶点着色器 SPIR-V
std::span<const uint32_t> frag_spirv; ///< 片段着色器 SPIR-V
std::span<const shader_binding_info> bindings; ///< 描述符绑定信息
custom_shader_render_mode render_mode;
// ========================================================================
// 共享参数(UBO)
// ========================================================================
const void* shared_params_data; ///< 共享参数数据指针
size_t shared_params_size; ///< 共享参数大小
// ========================================================================
// 实例数据
// ========================================================================
std::vector<default_instance_data> instances; ///< 实例数据数组
// ========================================================================
// 描述符更新
// ========================================================================
using update_descriptors_fn = std::function<void(const descriptor_update_context&)>;
update_descriptors_fn update_descriptors;
// ========================================================================
// 辅助方法
// ========================================================================
[[nodiscard]] uint32_t instance_count() const noexcept {
return static_cast<uint32_t>(instances.size());
}
};
// 更新 render_command_t variant
using render_command_t = std::variant<
rectangle_command,
text_command,
text_effect_command,
image_command,
border_rect_command,
mask_begin_command,
mask_end_command,
custom_shader_widget_command,
instanced_custom_shader_command // 新增
>;
5.2 实例化渲染节点
// ============================================================================
// 新增:实例化渲染节点
// ============================================================================
/// @brief 实例化自定义着色器渲染节点
struct instanced_custom_shader_node : render_node {
instanced_custom_shader_command command;
instanced_custom_shader_node(instanced_custom_shader_command cmd)
: command(std::move(cmd)) {
type = node_type::instanced_custom_shader;
}
};
5.3 实例数据 SSBO 管理器
// ============================================================================
// 实例数据缓冲管理器
// ============================================================================
/// @brief 实例数据 SSBO 管理器
class instance_buffer_manager {
public:
instance_buffer_manager(logical_device& device);
/// @brief 上传实例数据
/// @param instances 实例数据
/// @return SSBO 绑定信息
struct buffer_binding {
vk::Buffer buffer;
vk::DeviceSize offset;
vk::DeviceSize size;
};
auto upload_instances(std::span<const default_instance_data> instances)
-> buffer_binding;
/// @brief 帧结束时重置
void reset_frame();
private:
/// 环形缓冲区,每帧自动重置
struct ring_buffer {
vk::Buffer buffer;
vk::DeviceMemory memory;
void* mapped_ptr = nullptr;
vk::DeviceSize capacity = 0;
vk::DeviceSize current_offset = 0;
};
std::array<ring_buffer, 3> frame_buffers_; // 三缓冲
uint32_t current_frame_ = 0;
logical_device* device_ = nullptr;
static constexpr vk::DeviceSize INITIAL_CAPACITY = 1024 * 1024; // 1MB
};
5.4 扩展 Pipeline 缓存键
// ============================================================================
// 扩展 Pipeline 缓存键支持实例化
// ============================================================================
struct custom_pipeline_key_t {
uint32_t shader_id;
custom_shader_render_mode render_mode;
VkRenderPass render_pass;
size_t layout_hash;
bool instanced; // 新增:是否是实例化版本
auto operator==(const custom_pipeline_key_t& other) const -> bool {
return shader_id == other.shader_id &&
render_mode == other.render_mode &&
render_pass == other.render_pass &&
layout_hash == other.layout_hash &&
instanced == other.instanced; // 新增
}
};
6. 着色器设计
6.1 实例化顶点着色器模板
#version 450 core
/**
* @brief 实例化程序化四边形顶点着色器
*
* 支持实例化渲染,通过 gl_InstanceIndex 访问实例数据。
* 每个实例渲染一个四边形,支持旋转变换。
*/
// ============================================================================
// Push Constants(固定布局,所有实例共享)
// ============================================================================
layout(push_constant) uniform PushConstants {
vec2 scale; ///< NDC 缩放: (2/viewport_width, 2/viewport_height)
vec2 translate; ///< NDC 平移: (-1, -1)
} pc;
// ============================================================================
// 实例数据 SSBO
// ============================================================================
struct InstanceData {
vec4 rect; ///< x, y, width, height
vec4 transform; ///< rotation(弧度), anchor_x, anchor_y, scale
vec4 custom_0; ///< 用户自定义数据槽 0(如颜色)
vec4 custom_1; ///< 用户自定义数据槽 1(如参数)
};
layout(std430, set = 0, binding = 0) readonly buffer InstanceBuffer {
InstanceData instances[];
};
// ============================================================================
// 输出到片段着色器
// ============================================================================
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec4 v_color;
layout(location = 2) out vec4 v_custom;
layout(location = 3) flat out uint v_instance_id;
// ============================================================================
// 常量
// ============================================================================
const vec2 QUAD_POSITIONS[4] = vec2[4](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0)
);
const int QUAD_INDICES[6] = int[6](0, 1, 2, 2, 1, 3);
// ============================================================================
// 变换辅助函数
// ============================================================================
/// @brief 2D 旋转矩阵
mat2 rotate2D(float angle) {
float c = cos(angle);
float s = sin(angle);
return mat2(c, -s, s, c);
}
// ============================================================================
// 主函数
// ============================================================================
void main() {
// 获取实例数据
InstanceData inst = instances[gl_InstanceIndex];
// 解析变换参数
float rotation = inst.transform.x;
vec2 anchor = inst.transform.yz; // (anchor_x, anchor_y)
float inst_scale = inst.transform.w;
// 如果 scale 为 0,使用默认值 1.0
if (inst_scale == 0.0) inst_scale = 1.0;
// 计算顶点位置
int quad_vertex = QUAD_INDICES[gl_VertexIndex];
vec2 local_pos = QUAD_POSITIONS[quad_vertex];
// 应用变换:
// 1. 将顶点移到锚点为中心
vec2 centered = local_pos - anchor;
// 2. 应用缩放
centered *= inst_scale;
// 3. 应用旋转
centered = rotate2D(rotation) * centered;
// 4. 移回原位并应用实例位置和大小
vec2 transformed = (centered + anchor) * inst.rect.zw + inst.rect.xy;
// 输出
v_uv = local_pos;
v_color = inst.custom_0;
v_custom = inst.custom_1;
v_instance_id = gl_InstanceIndex;
// 转换到 NDC
vec2 ndc = transformed * pc.scale + pc.translate;
gl_Position = vec4(ndc, 0.0, 1.0);
}
变换流程说明:
原始四边形 (0,0)-(1,1)
↓ 减去锚点 (centered)
以锚点为中心
↓ 乘以缩放因子
缩放后
↓ 乘以旋转矩阵
旋转后
↓ 加回锚点,乘以 size,加上 position
最终屏幕位置
默认值:
rotation = 0.0:不旋转anchor = (0.5, 0.5):中心锚点scale = 1.0:原始大小
6.2 实例化片段着色器示例(渐变)
#version 450 core
/**
* @brief 实例化渐变着色器
*/
// ============================================================================
// 输入
// ============================================================================
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_color; ///< 起始颜色(来自实例数据)
layout(location = 2) in vec4 v_custom; ///< 自定义参数
layout(location = 3) flat in uint v_instance_id;
// ============================================================================
// UBO(所有实例共享)
// ============================================================================
layout(set = 0, binding = 1) uniform SharedParams {
vec4 end_color; ///< 渐变结束颜色
float gradient_type; ///< 渐变类型
float gradient_angle;///< 渐变角度
vec2 _padding;
} params;
// ============================================================================
// 输出
// ============================================================================
layout(location = 0) out vec4 out_color;
// ============================================================================
// 主函数
// ============================================================================
void main() {
// 计算渐变因子
float t;
uint type = uint(params.gradient_type);
if (type == 0u) {
t = v_uv.x; // 水平
} else if (type == 1u) {
t = v_uv.y; // 垂直
} else if (type == 2u) {
vec2 dir = vec2(cos(params.gradient_angle), sin(params.gradient_angle));
t = dot(v_uv - 0.5, dir) + 0.5; // 角度
} else {
t = length(v_uv - 0.5) * 2.0; // 径向
}
t = clamp(t, 0.0, 1.0);
// 混合颜色:v_color(实例起始色)到 end_color(共享结束色)
out_color = mix(v_color, params.end_color, t);
}
6.3 描述符集布局
// 实例化着色器描述符布局
// Set 0:
// Binding 0: Instance SSBO (storage buffer, vertex stage)
// Binding 1: Shared Params UBO (uniform buffer, fragment stage)
// Binding 2+: 纹理等(可选)
7. 实现步骤清单
7.0 Phase 0: 代码生成工具扩展(约 1 周)
| 步骤 | 任务 | 产出物 |
|---|---|---|
| 0.1 | 添加 gl_InstanceIndex 检测逻辑 |
修改 spirv_parser.py |
| 0.2 | 设计实例数据结构生成规则 | 修改 type_mapping.py |
| 0.3 | 实现 instanced_shader_spec 生成 |
修改 code_generator.py |
| 0.4 | 创建实例化顶点着色器模板 | shaders/widget/instanced_quad.vert.glsl |
| 0.5 | 更新 CMake 编译规则 | 修改 compile_shaders.cmake |
7.1 Phase 1: 基础设施(约 2 周)
| 步骤 | 任务 | 产出物 |
|---|---|---|
| 1.1 | 定义 default_instance_data 结构 |
src/render/instance_types.h |
| 1.2 | 实现 instance_buffer_manager |
src/render/instance_buffer_manager.h/cpp |
| 1.3 | 创建实例化顶点着色器模板 | shaders/widget/instanced_procedural.vert.glsl |
| 1.4 | 扩展 custom_pipeline_key_t |
修改 custom_shader_widget_renderer.h |
| 1.5 | 添加实例化 Pipeline 创建逻辑 | 修改 custom_shader_widget_renderer.cpp |
7.2 Phase 2: 合批收集器(约 1.5 周)
| 步骤 | 任务 | 产出物 |
|---|---|---|
| 2.1 | 定义 instanced_custom_shader_command |
修改 render_command.h |
| 2.2 | 实现 instanced_batch_collector |
src/render/pipeline/instanced_batch_collector.h/cpp |
| 2.3 | 集成到 render_tree_builder |
修改 render_tree_builder.cpp |
| 2.4 | 添加 instanced_custom_shader_node |
修改 render_tree.h |
7.3 Phase 3: 渲染器扩展(约 1.5 周)
| 步骤 | 任务 | 产出物 |
|---|---|---|
| 3.1 | 实现 render_instanced() 方法 |
修改 custom_shader_widget_renderer.cpp |
| 3.2 | 集成到 render_tree_executor |
修改 render_tree_executor.cpp |
| 3.3 | 实现实例化描述符集布局 | 修改渲染器 |
7.4 Phase 4: 验证与优化(约 1 周)
| 步骤 | 任务 | 产出物 |
|---|---|---|
| 4.1 | 编写单元测试 | tests/instanced_rendering_test.cpp |
| 4.2 | 性能基准测试 | 基准测试报告 |
| 4.3 | 边界条件处理 | 代码完善 |
| 4.4 | 文档更新 | 更新使用文档 |
8. 风险评估与缓解策略
8.1 风险矩阵
| 风险 ID | 描述 | 可能性 | 影响 | 缓解策略 |
|---|---|---|---|---|
| R1 | 实例数据大小超过缓冲区限制 | 低 | 高 | 动态扩容,分批绘制 |
| R2 | 不同控件的参数无法统一 | 中 | 中 | 使用通用 custom_data 槽 |
| R3 | 裁剪区域分组导致合批效果有限 | 中 | 低 | 裁剪区域合并优化 |
| R4 | 着色器修改破坏现有效果 | 低 | 高 | 保留非实例化路径 |
| R5 | SSBO 性能在某些 GPU 上较差 | 低 | 中 | 提供顶点属性备选方案 |
8.2 缓解策略详述
R1: 缓冲区动态扩容
auto instance_buffer_manager::upload_instances(
std::span<const default_instance_data> instances
) -> buffer_binding {
auto& buf = frame_buffers_[current_frame_];
vk::DeviceSize required_size = instances.size_bytes();
// 检查是否需要扩容
if (buf.current_offset + required_size > buf.capacity) {
// 策略1: 如果单次上传过大,分批处理
if (required_size > buf.capacity) {
// 返回特殊标记,渲染器负责分批
return {.buffer = VK_NULL_HANDLE, .size = required_size};
}
// 策略2: 等待下一帧(极少发生)
// 或扩容缓冲区
}
// 正常上传...
}
R4: 保留回退路径
void custom_shader_widget_renderer::render_impl(
vk::CommandBuffer cmd,
const custom_shader_widget_command& command,
...
) {
// 检查是否支持实例化
if (can_use_instancing(command)) {
// 实例化路径(由 executor 在合批后调用 render_instanced)
// 单个命令走传统路径
}
// 回退到传统单实例渲染
render_single_instance(cmd, command, ...);
}
8.3 性能预期
| 场景 | 当前性能 | 优化后预期 | 提升幅度 |
|---|---|---|---|
| 100 个相同渐变 | 100 Draw Calls | 1 Draw Call | 99% |
| 100 个不同裁剪区域 | 100 Draw Calls | ~10-20 Draw Calls | 80-90% |
| 混合场景(50 相同 + 50 不同) | 100 Draw Calls |