Files
mirage/plans/instanced_rendering_api_design.md
2025-12-25 13:20:16 +08:00

32 KiB
Raw Permalink Blame History

Custom Shader Widget 实例化渲染 API 设计文档

版本: 1.1 日期: 2025-01 状态: 设计草案 关联问题: P2-003 (Custom Shader Widget 无法合批)


目录

  1. 设计目标与约束
  2. API 设计方案
  3. 代码生成工具集成
  4. 数据流架构
  5. 关键数据结构
  6. 着色器设计
  7. 实现步骤清单
  8. 风险评估与缓解策略

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 (GradientParamsUBO 参数)
//   ├── 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): 锚点 X0.0 = 左, 0.5 = 中, 1.0 = 右)
/// - transform.z (anchor_y): 锚点 Y0.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_spectools/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(...)

检测逻辑

  1. 解析顶点着色器 SPIR-V
  2. 查找 OpDecorate %id BuiltIn InstanceIndex 指令
  3. 如果存在,标记该着色器支持实例化
  4. 生成相应的 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 字节边界
};

生成规则:

  1. 强制字段vec4f_t rect 始终作为第一个字段
  2. 复制字段:从 push_constant_type 复制所有成员
  3. 对齐填充:确保总大小是 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_typeinstanced_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