20 KiB
⚠️ 此文档已废弃
Mode Data 层已被完全移除。本文档保留作为历史记录。
当前状态:mode_data 相关的 API 和概念已不再使用,请参考新的架构文档。
Mode Data 接口迁移指南(归档)
📅 归档日期:2025-01 🔒 状态:已废弃 - Mode Data 层已被移除
历史背景
本指南原用于描述从旧的 Mode Data 接口迁移到统一接口的过程。现在 Mode Data 层已被完全移除,本文档仅作为历史参考保留。
迁移历程
- v1.x:原有
mode_data系统,包含get_mode_data_ptr()、is_mode_data_dirty()、clear_mode_data_dirty()等虚函数 - v2.x:mode_data 层移除,所有相关接口已废弃
- 当前:mode_data 概念完全移除,相关代码已重构
架构变化摘要
移除的组件
| 组件 | 说明 | 替代方案 |
|---|---|---|
mode_data_mixin |
CRTP Mixin 用于 Mode Data 管理 | 已移除 |
get_mode_data_ptr() |
基类虚函数获取 Mode Data | 使用 get_params_data() 替代 |
is_mode_data_dirty() |
Mode Data 脏标志检查 | 使用 is_params_dirty() 替代 |
clear_mode_data_dirty() |
清除 Mode Data 脏标志 | 使用 clear_params_dirty() 替代 |
compute_mode_data() |
统一计算 Mode Data | 已移除 |
mode_data() |
获取 Mode Data 引用 | 已移除 |
保留的组件
| 组件 | 说明 |
|---|---|
get_params_data() |
获取 UBO 参数数据 |
is_params_dirty() |
参数脏标志检查 |
clear_params_dirty() |
清除参数脏标志 |
get_push_constant_effect_data() |
Push Constants 效果参数 |
get_effect_rect() |
效果区域 |
新的架构
当前架构使用简化的参数管理:
class custom_shader_widget_base : public leaf_widget_base {
protected:
// UBO 参数(必需)
[[nodiscard]] virtual auto get_params_data() const
-> std::pair<const void*, size_t> = 0;
[[nodiscard]] virtual auto is_params_dirty() const noexcept -> bool = 0;
virtual void clear_params_dirty() = 0;
// Push Constants 效果参数(可选)
[[nodiscard]] virtual auto get_push_constant_effect_data() const
-> std::pair<const void*, size_t>;
[[nodiscard]] virtual auto is_push_constant_effect_dirty() const noexcept -> bool;
virtual void clear_push_constant_effect_dirty();
// 效果区域(可选)
[[nodiscard]] virtual auto get_effect_rect() const noexcept -> vec4f_t;
virtual void set_effect_rect(const vec4f_t& rect);
};
常见问题
Q: 我的代码使用了 mode_data 相关的 API 怎么办?
A: 这些 API 已被移除,请使用以下替代方案:
| 旧 API | 新 API |
|---|---|
get_mode_data_ptr() |
get_params_data() |
mode_data() |
params() |
mark_mode_data_dirty() |
mark_params_dirty() |
Q: 如何获取控件位置和大小信息?
A: 使用标准的布局接口:
// 获取全局位置
auto pos = widget->get_global_position();
// 获取大小
auto size = widget->get_size();
// 获取效果区域(用于 post_process 模式)
auto rect = widget->get_effect_rect();
相关文档
- CUSTOM_SHADER_WIDGET_ARCHITECTURE.md - 当前架构设计
- CUSTOM_SHADER_WIDGET.md - 使用指南
文档版本: 2.0(归档版) 最后更新: 2025-01
3. 逐类迁移指南
3.1 基类变更:custom_shader_widget_base
基类新增了三个纯虚函数,所有自定义子类必须实现:
// 新增的纯虚函数(位于 custom_shader_widget_base.h:45-53)
[[nodiscard]] virtual auto get_mode_data_ptr() const
-> std::pair<const void*, size_t> = 0;
[[nodiscard]] virtual auto is_mode_data_dirty() const noexcept -> bool = 0;
virtual void clear_mode_data_dirty() = 0;
如果您直接继承 custom_shader_widget_base:需要实现这三个方法。
如果您继承模板子类(推荐):这些方法已由模板类实现,无需额外操作。
3.2 procedural_shader_widget<ShaderSpec>
方法重命名
| 旧方法 | 新方法 | 说明 |
|---|---|---|
widget_color_component(i) |
widget_color_at(i) + set_widget_color_at(i, v) |
分离读写 |
get_procedural_mode_data() |
compute_mode_data() |
统一命名 |
新增方法
mode_data()- 获取 Mode Data 只读引用mark_mode_data_dirty()- 标记 Mode Data 已修改
迁移示例
读取颜色分量:
// ❌ 旧代码(已弃用)
float r = widget.widget_color_component(0);
// ✅ 新代码
float r = widget.widget_color_at(0);
// ✅ 或直接访问整个数组
auto color = widget.get_widget_color();
float r = color[0];
修改颜色分量:
// ❌ 旧代码(已弃用)
widget.widget_color_component(0) = 1.0f;
widget.mark_mode_data_dirty(); // 容易忘记调用
// ✅ 新代码(自动标记脏标志)
widget.set_widget_color_at(0, 1.0f);
获取 Mode Data:
// ❌ 旧代码(已弃用)
auto data = widget.get_procedural_mode_data();
// ✅ 新代码
auto data = widget.compute_mode_data();
3.3 post_process_shader_widget<ShaderSpec>
方法重命名
| 旧方法 | 新方法 | 说明 |
|---|---|---|
source_uv_component(i) |
source_uv_at(i) + set_source_uv_at(i, v) |
分离读写 |
get_post_process_mode_data() |
compute_mode_data() |
统一命名 |
新增方法
mode_data()- 获取 Mode Data 只读引用mark_mode_data_dirty()- 标记 Mode Data 已修改
迁移示例
读取 UV 分量:
// ❌ 旧代码(已弃用)
float u0 = widget.source_uv_component(0);
// ✅ 新代码
float u0 = widget.source_uv_at(0);
// ✅ 或使用完整 UV rect
auto uv = widget.get_source_uv_rect();
修改 UV 分量:
// ❌ 旧代码(已弃用)
widget.source_uv_component(2) = 0.5f;
widget.mark_mode_data_dirty();
// ✅ 新代码(自动标记脏标志)
widget.set_source_uv_at(2, 0.5f);
获取 Mode Data:
// ❌ 旧代码(已弃用)
auto data = widget.get_post_process_mode_data();
// ✅ 新代码
auto data = widget.compute_mode_data();
3.4 mask_shader_widget<ShaderSpec>
方法重命名
| 旧方法 | 新方法 | 说明 |
|---|---|---|
corner_radius_component(i) |
corner_radius_at(i) + set_corner_radius_at(i, v) |
分离读写 |
get_mask_mode_data() |
compute_mode_data() |
统一命名 |
新增方法
mode_data()- 获取 Mode Data 只读引用mark_mode_data_dirty()- 标记 Mode Data 已修改
访问级别变更
is_mode_data_dirty()- 从 protected 改为 publicclear_mode_data_dirty()- 从 protected 改为 public
迁移示例
读取圆角半径:
// ❌ 旧代码(已弃用)
float tl = widget.corner_radius_component(0);
// ✅ 新代码
float tl = widget.corner_radius_at(0);
// ✅ 或使用完整数组
auto radii = widget.get_corner_radii();
修改圆角半径:
// ❌ 旧代码(已弃用)
widget.corner_radius_component(0) = 10.0f;
widget.mark_mode_data_dirty();
// ✅ 新代码(自动标记脏标志)
widget.set_corner_radius_at(0, 10.0f);
// ✅ 或设置所有四角
widget.set_corner_radii(10.0f, 10.0f, 10.0f, 10.0f);
widget.set_uniform_corner_radius(10.0f); // 等效
获取 Mode Data:
// ❌ 旧代码(已弃用)
auto data = widget.get_mask_mode_data();
// ✅ 新代码
auto data = widget.compute_mode_data();
3.5 full_custom_shader_widget<ShaderSpec, VertexT>
方法重命名
| 旧方法 | 新方法 | 说明 |
|---|---|---|
user_data() |
user_data_at(i) + set_user_data_at(i, v) |
分离读写 |
set_user_data(i, v) |
set_user_data_at(i, v) |
命名统一 |
get_full_custom_mode_data() |
compute_mode_data() |
统一命名 |
新增方法
mode_data()- 获取 Mode Data 只读引用mark_mode_data_dirty()- 标记 Mode Data 已修改
迁移示例
读取用户数据:
// ❌ 旧代码(已弃用)
float val = widget.user_data()[0];
// ✅ 新代码
float val = widget.user_data_at(0);
修改用户数据:
// ❌ 旧代码(已弃用)
widget.user_data()[0] = 42.0f;
widget.mark_mode_data_dirty();
// ❌ 旧代码(已弃用)
widget.set_user_data(0, 42.0f);
// ✅ 新代码(自动标记脏标志)
widget.set_user_data_at(0, 42.0f);
获取 Mode Data:
// ❌ 旧代码(已弃用)
auto data = widget.get_full_custom_mode_data();
// ✅ 新代码
auto data = widget.compute_mode_data();
4. 常见迁移场景
场景 1:读取数组元素
// ===== procedural_shader_widget =====
// 旧:返回引用,无边界检查
float r = widget.widget_color_component(0);
// 新:返回值,带边界检查
float r = widget.widget_color_at(0);
// ===== post_process_shader_widget =====
// 旧
float u = widget.source_uv_component(0);
// 新
float u = widget.source_uv_at(0);
// ===== mask_shader_widget =====
// 旧
float radius = widget.corner_radius_component(0);
// 新
float radius = widget.corner_radius_at(0);
// ===== full_custom_shader_widget =====
// 旧
float data = widget.user_data()[0];
// 新
float data = widget.user_data_at(0);
场景 2:修改数组元素
// ===== 通用模式 =====
// 旧:通过引用修改,需手动标记脏
widget.xxx_component(i) = value;
widget.mark_mode_data_dirty();
// 新:通过 setter 修改,自动标记脏
widget.set_xxx_at(i, value);
场景 3:遍历数组
// ===== procedural_shader_widget =====
// 旧
for (size_t i = 0; i < 4; ++i) {
process(widget.widget_color_component(i));
}
// 新:推荐使用完整数组访问
auto color = widget.get_widget_color();
for (float c : color) {
process(c);
}
// 或使用索引访问(带边界检查)
for (size_t i = 0; i < 4; ++i) {
process(widget.widget_color_at(i));
}
场景 4:自定义子类实现
如果您有直接继承 custom_shader_widget_base 的自定义类,需要实现新增的纯虚函数:
class my_custom_widget : public custom_shader_widget_base {
public:
// 必须实现这三个纯虚函数
[[nodiscard]] auto get_mode_data_ptr() const
-> std::pair<const void*, size_t> override {
return {&my_mode_data_, sizeof(my_mode_data_)};
}
[[nodiscard]] auto is_mode_data_dirty() const noexcept -> bool override {
return mode_data_dirty_;
}
void clear_mode_data_dirty() override {
mode_data_dirty_ = false;
}
private:
my_mode_data_type my_mode_data_{};
bool mode_data_dirty_ = true;
};
5. 渲染器迁移
5.1 使用新的 get_mode_data_ptr() 虚函数
旧渲染器代码可能需要针对不同模式做类型判断:
// ❌ 旧代码:需要类型判断
if (mode == procedural) {
auto data = static_cast<procedural_widget*>(widget)->get_procedural_mode_data();
upload_push_constants(&data, sizeof(data));
} else if (mode == post_process) {
auto data = static_cast<post_process_widget*>(widget)->get_post_process_mode_data();
upload_push_constants(&data, sizeof(data));
}
// ... 更多模式判断
新代码可以统一处理:
// ✅ 新代码:通过基类统一访问
auto [data_ptr, data_size] = widget->get_mode_data_ptr();
if (data_ptr && data_size > 0) {
upload_push_constants(data_ptr, data_size);
}
5.2 脏标志管理
渲染器可以利用 is_mode_data_dirty() 优化更新:
// 仅在数据变化时更新 GPU
if (widget->is_mode_data_dirty()) {
auto [data_ptr, data_size] = widget->get_mode_data_ptr();
update_gpu_buffer(data_ptr, data_size);
widget->clear_mode_data_dirty();
}
5.3 渲染器实现参考
void custom_shader_widget_renderer::update_mode_data(
custom_shader_widget_base* widget,
vk::CommandBuffer cmd
) {
// 1. 检查是否需要更新
if (!widget->is_mode_data_dirty()) {
return;
}
// 2. 获取 Mode Data(Layer 2)
auto [mode_data, mode_data_size] = widget->get_mode_data_ptr();
// 3. 上传 Push Constants
if (mode_data && mode_data_size > 0) {
cmd.pushConstants(
pipeline_layout_,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
push_constant_offsets::MODE_DATA, // offset 80
static_cast<uint32_t>(mode_data_size),
mode_data
);
}
// 4. 清除脏标志
widget->clear_mode_data_dirty();
}
6. 测试验证清单
完成迁移后,请验证以下功能点:
6.1 编译检查
- 无弃用警告(使用
-Werror=deprecated-declarations检查) - 所有自定义子类正确实现纯虚函数
- 模板实例化无错误
6.2 功能测试
-
procedural 模式
widget_color_at()正确读取颜色set_widget_color_at()正确修改颜色并标记脏compute_mode_data()返回正确的 widget_rect
-
post_process 模式
source_uv_at()正确读取 UVset_source_uv_at()正确修改 UV 并标记脏compute_mode_data()返回正确的数据
-
mask 模式
corner_radius_at()正确读取半径set_corner_radius_at()正确修改半径并标记脏compute_mode_data()自动计算 mask_center 和 mask_size
-
full_custom 模式
user_data_at()正确读取数据set_user_data_at()正确修改数据并标记脏- 顶点/索引缓冲正常工作
6.3 渲染器测试
get_mode_data_ptr()返回正确的指针和大小is_mode_data_dirty()正确反映状态clear_mode_data_dirty()正确清除标志- Push Constants 正确上传到 GPU
- 渲染结果与迁移前一致
6.4 边界检查测试
- 越界索引抛出
std::out_of_range - 错误信息包含有效索引范围
// 测试代码示例
try {
widget.widget_color_at(4); // 应该抛出异常
FAIL("Expected out_of_range exception");
} catch (const std::out_of_range& e) {
EXPECT_THAT(e.what(), HasSubstr("index"));
}
7. 常见问题解答(FAQ)
Q1: 为什么要弃用 *_component() 方法?
A: 旧方法存在几个问题:
- 返回引用无法进行边界检查,可能导致未定义行为
- 修改后需要手动调用
mark_mode_data_dirty(),容易遗忘 - 命名不够清晰(
componentvsat)
新方法分离读写操作,setter 自动标记脏标志,更加安全和便捷。
Q2: 新的 *_at() 方法性能如何?
A: 边界检查的开销极小(一次比较和条件跳转),在 Release 构建中对性能影响可忽略。安全性的提升远超这点开销。
如果您确实需要零开销访问,可以使用:
// 通过 mode_data() 直接访问底层数组(无边界检查)
const auto& data = widget.mode_data();
float r = data.widget_color[0]; // 直接访问
Q3: 可以同时使用新旧 API 吗?
A: 可以,但不建议。混用可能导致:
- 代码风格不一致
- 部分修改漏调用
mark_mode_data_dirty() - 增加维护成本
建议:在同一个文件或类中统一使用新 API。
Q4: 如何快速查找需要迁移的代码?
A: 使用正则表达式搜索:
# 查找 widget_color_component 调用
grep -rn "widget_color_component" src/
# 查找 source_uv_component 调用
grep -rn "source_uv_component" src/
# 查找 corner_radius_component 调用
grep -rn "corner_radius_component" src/
# 查找旧的 user_data 用法
grep -rn "user_data()" src/
# 查找旧的 get_*_mode_data 调用
grep -rn "get_procedural_mode_data\|get_post_process_mode_data\|get_mask_mode_data\|get_full_custom_mode_data" src/
或者启用编译器的弃用警告:
# GCC/Clang
cmake -DCMAKE_CXX_FLAGS="-Wdeprecated-declarations" ..
# MSVC
cmake -DCMAKE_CXX_FLAGS="/W4" ..
Q5: mode_data() 返回的是只读引用,如何批量修改?
A: 有几种方式:
// 方式1:使用专用的批量设置方法
widget.set_widget_color(1.0f, 0.5f, 0.0f, 1.0f);
widget.set_corner_radii(10.0f, 10.0f, 10.0f, 10.0f);
// 方式2:使用数组参数
std::array<float, 4> color = {1.0f, 0.5f, 0.0f, 1.0f};
widget.set_widget_color(color);
// 方式3:链式调用单个设置
widget.set_widget_color_at(0, 1.0f)
.set_widget_color_at(1, 0.5f)
.set_widget_color_at(2, 0.0f)
.set_widget_color_at(3, 1.0f);
Q6: 子类如何重写 compute_mode_data()?
A: compute_mode_data() 是一个虚函数,子类可以重写以自定义行为:
class my_gradient_widget : public procedural_shader_widget<gradient_spec> {
protected:
[[nodiscard]] auto compute_mode_data() const -> procedural_mode_data override {
// 调用基类实现
auto data = procedural_shader_widget::compute_mode_data();
// 自定义修改
data.widget_color[3] = calculate_dynamic_alpha();
return data;
}
};
Q7: 迁移后旧代码还能编译吗?
A: 能,但会产生编译器警告。示例:
warning: 'widget_color_component' is deprecated: Use widget_color_at(index)
or set_widget_color_at(index, value) instead [-Wdeprecated-declarations]
Q8: 弃用的 API 什么时候会被移除?
A: 计划在 v3.0 版本移除。建议:
- 现在开始逐步迁移
- 在 v2.x 周期内完成所有迁移
- 使用 CI 检测弃用警告,防止新代码使用旧 API
附录:变更速查表
基类纯虚函数
| 方法 | 返回类型 | 说明 |
|---|---|---|
get_mode_data_ptr() |
std::pair<const void*, size_t> |
获取 Mode Data 原始指针和大小 |
is_mode_data_dirty() |
bool |
检查 Mode Data 是否需要更新 |
clear_mode_data_dirty() |
void |
清除脏标志 |
方法重命名对照表
procedural_shader_widget
| 旧方法 | 新方法(读取) | 新方法(写入) |
|---|---|---|
widget_color_component(i) |
widget_color_at(i) |
set_widget_color_at(i, v) |
get_procedural_mode_data() |
compute_mode_data() |
- |
post_process_shader_widget
| 旧方法 | 新方法(读取) | 新方法(写入) |
|---|---|---|
source_uv_component(i) |
source_uv_at(i) |
set_source_uv_at(i, v) |
get_post_process_mode_data() |
compute_mode_data() |
- |
mask_shader_widget
| 旧方法 | 新方法(读取) | 新方法(写入) |
|---|---|---|
corner_radius_component(i) |
corner_radius_at(i) |
set_corner_radius_at(i, v) |
get_mask_mode_data() |
compute_mode_data() |
- |
full_custom_shader_widget
| 旧方法 | 新方法(读取) | 新方法(写入) |
|---|---|---|
user_data() (数组) |
user_data_at(i) |
set_user_data_at(i, v) |
set_user_data(i, v) |
- | set_user_data_at(i, v) |
get_full_custom_mode_data() |
compute_mode_data() |
- |
新增公开方法
| 类 | 方法 | 说明 |
|---|---|---|
| 所有模板类 | mode_data() |
获取 Mode Data 只读引用 |
| 所有模板类 | mark_mode_data_dirty() |
标记 Mode Data 已修改 |
访问级别变更
| 类 | 方法 | 旧访问级别 | 新访问级别 |
|---|---|---|---|
mask_shader_widget |
is_mode_data_dirty() |
protected | public |
mask_shader_widget |
clear_mode_data_dirty() |
protected | public |
post_process_shader_widget |
is_mode_data_dirty() |
protected | public |
post_process_shader_widget |
clear_mode_data_dirty() |
protected | public |
文档版本: 1.0 最后更新: 2025-01