diff --git a/.gitmodules b/.gitmodules index 9c47737..d51be26 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "nvapi/sdk"] - path = nvapi/sdk - url = https://github.com/NVIDIA/nvapi.git [submodule "nvapi/sdk/nvapi"] path = nvapi/sdk/nvapi url = https://github.com/NVIDIA/nvapi.git diff --git a/plans/push_constant_base_static_check_design.md b/plans/push_constant_base_static_check_design.md new file mode 100644 index 0000000..d21b6b7 --- /dev/null +++ b/plans/push_constant_base_static_check_design.md @@ -0,0 +1,1043 @@ + +# Push Constant 基础部分存储与静态检查技术方案 + +## 1. 背景与需求分析 + +### 1.1 当前架构 + +Push Constants 基础部分(前80字节)在 [`push_constants_base`](src/render/uniform_types.h:24) 中定义: + +```cpp +struct push_constants_base { + float projection[16]; // 偏移 0, 大小 64 + float viewport_size[2]; // 偏移 64, 大小 8 + float time; // 偏移 72, 大小 4 + float _pad0; // 偏移 76, 大小 4 +}; +``` + +### 1.2 当前处理方式的问题 + +1. **解析器** ([`spirv_parser.py`](tools/spirv_parser.py:460)) 将 `offset < 80` 的成员视为基础部分并忽略 +2. **代码生成器** 只生成效果部分结构体,基础部分信息丢失 +3. **无法验证** 着色器声明的基础部分与 C++ 端定义是否一致 + +### 1.3 不同着色器类型的布局差异 + +| 着色器类型 | 布局特点 | 示例 | +|-----------|---------|------| +| post_effect | 完整基础部分 + 效果参数 | [`blur.frag.glsl`](shaders/post_effect/blur.frag.glsl:8) | +| widget | 不使用 push_constant | [`wave.frag.glsl`](shaders/widget/wave.frag.glsl:13) | +| text vertex | 不同布局(有 screen_resolution) | [`mtsdf_text.vert.glsl`](shaders/widget/mtsdf_text.vert.glsl:18) | + +--- + +## 2. 数据结构修改方案 + +### 2.1 Python 端数据结构 + +#### 2.1.1 新增 `PushConstantBaseMember` 枚举 + +在 [`types.py`](tools/types.py) 中添加: + +```python +from enum import Flag, auto + +class PushConstantBaseMember(Flag): + """Push Constants 基础部分成员标志""" + NONE = 0 + PROJECTION = auto() # mat4 projection + VIEWPORT_SIZE = auto() # vec2 viewport_size + TIME = auto() # float time + PAD0 = auto() # float _pad0 + + # 预定义的组合 + STANDARD = PROJECTION | VIEWPORT_SIZE | TIME | PAD0 + VERTEX_ONLY = PROJECTION + POST_EFFECT = PROJECTION | VIEWPORT_SIZE | TIME | PAD0 +``` + +#### 2.1.2 新增 `PushConstantBaseMemberInfo` 数据类 + +```python +@dataclass +class PushConstantBaseMemberInfo: + """Push Constants 基础部分单个成员信息""" + name: str # 成员名称(如 "projection") + member_flag: PushConstantBaseMember # 成员标志 + offset: int # 实际偏移量 + size: int # 实际大小 + expected_offset: int # 期望偏移量 + expected_size: int # 期望大小 + type_name: str # C++ 类型名称 + is_valid: bool = True # 是否与期望匹配 +``` + +#### 2.1.3 新增 `PushConstantBaseInfo` 数据类 + +```python +@dataclass +class PushConstantBaseInfo: + """Push Constants 基础部分完整信息""" + members: List[PushConstantBaseMemberInfo] = field(default_factory=list) + present_flags: PushConstantBaseMember = PushConstantBaseMember.NONE + total_size: int = 0 + is_standard_layout: bool = False # 是否为标准布局 + + # 期望布局定义 + EXPECTED_LAYOUT = { + 'projection': (0, 64), # (offset, size) + 'viewport_size': (64, 8), + 'time': (72, 4), + '_pad0': (76, 4), + } + EXPECTED_TOTAL_SIZE = 80 +``` + +#### 2.1.4 修改 `PushConstantInfo` 数据类 + +```python +@dataclass +class PushConstantInfo: + """Push Constants 完整信息""" + name: str # 结构体名称 + struct_type: StructTypeInfo # 完整结构体类型 + base_offset: int = PUSH_CONSTANT_BASE_OFFSET + base_info: Optional[PushConstantBaseInfo] = None # 新增:基础部分信息 + effect_members: List[MemberInfo] = field(default_factory=list) +``` + +### 2.2 C++ 端数据结构 + +#### 2.2.1 Push Constant 基础布局特征结构 + +在生成的头文件中添加: + +```cpp +/// @brief Push Constants 基础部分布局信息(由着色器工具自动生成) +struct {shader_name}_push_constant_base_layout { + // 成员存在性标志 + static constexpr bool has_projection = true; + static constexpr bool has_viewport_size = true; + static constexpr bool has_time = true; + static constexpr bool has_pad0 = true; + + // 成员偏移量 + static constexpr size_t projection_offset = 0; + static constexpr size_t viewport_size_offset = 64; + static constexpr size_t time_offset = 72; + static constexpr size_t pad0_offset = 76; + + // 成员大小 + static constexpr size_t projection_size = 64; + static constexpr size_t viewport_size_size = 8; + static constexpr size_t time_size = 4; + static constexpr size_t pad0_size = 4; + + // 总大小 + static constexpr size_t total_size = 80; + + // 布局类型标识 + static constexpr bool is_standard_layout = true; +}; +``` + +#### 2.2.2 在 `uniform_types.h` 中添加期望布局定义 + +```cpp +namespace mirage { + +/// @brief Push Constants 标准基础部分期望布局 +/// +/// 所有使用标准基础部分的着色器必须匹配此布局。 +struct expected_push_constant_base_layout { + static constexpr bool has_projection = true; + static constexpr bool has_viewport_size = true; + static constexpr bool has_time = true; + static constexpr bool has_pad0 = true; + + static constexpr size_t projection_offset = 0; + static constexpr size_t viewport_size_offset = 64; + static constexpr size_t time_offset = 72; + static constexpr size_t pad0_offset = 76; + + static constexpr size_t projection_size = 64; + static constexpr size_t viewport_size_size = 8; + static constexpr size_t time_size = 4; + static constexpr size_t pad0_size = 4; + + static constexpr size_t total_size = 80; +}; + +// 验证 push_constants_base 与期望布局一致 +static_assert(offsetof(push_constants_base, projection) == + expected_push_constant_base_layout::projection_offset); +static_assert(offsetof(push_constants_base, viewport_size) == + expected_push_constant_base_layout::viewport_size_offset); +static_assert(offsetof(push_constants_base, time) == + expected_push_constant_base_layout::time_offset); +static_assert(offsetof(push_constants_base, _pad0) == + expected_push_constant_base_layout::pad0_offset); + +} // namespace mirage +``` + +--- + +## 3. 解析器修改方案 + +### 3.1 修改 `extract_push_constants()` 函数 + +在 [`spirv_parser.py`](tools/spirv_parser.py:424) 中修改: + +```python +# 基础成员名称到标志的映射 +BASE_MEMBER_MAP = { + 'projection': (PushConstantBaseMember.PROJECTION, 0, 64), + 'viewport_size': (PushConstantBaseMember.VIEWPORT_SIZE, 64, 8), + 'time': (PushConstantBaseMember.TIME, 72, 4), + '_pad0': (PushConstantBaseMember.PAD0, 76, 4), +} + +def _identify_base_member(member: MemberInfo, type_map: Dict[int, TypeInfo]) -> Optional[PushConstantBaseMemberInfo]: + """识别基础部分成员""" + if member.offset >= PUSH_CONSTANT_BASE_OFFSET: + return None + + # 计算成员大小 + member_size = calculate_std430_size(member.resolved_type, type_map) + cpp_type = spirv_type_to_cpp(member.resolved_type, type_map) + + # 通过名称或偏移量匹配 + for name, (flag, expected_offset, expected_size) in BASE_MEMBER_MAP.items(): + if member.name == name or member.offset == expected_offset: + is_valid = (member.offset == expected_offset and member_size == expected_size) + return PushConstantBaseMemberInfo( + name=member.name, + member_flag=flag, + offset=member.offset, + size=member_size, + expected_offset=expected_offset, + expected_size=expected_size, + type_name=cpp_type, + is_valid=is_valid, + ) + + return None + + +def extract_push_constants(reflection: SPIRVReflection) -> Optional[PushConstantInfo]: + """从变量中提取 Push Constants 信息(包含基础部分)""" + + # ... 现有的变量查找代码 ... + + # 分离基础部分和效果部分 + base_members_info = [] + present_flags = PushConstantBaseMember.NONE + effect_members = [] + + for member in struct_type.members: + base_member_info = _identify_base_member(member, reflection.types) + if base_member_info: + base_members_info.append(base_member_info) + present_flags |= base_member_info.member_flag + elif member.offset >= PUSH_CONSTANT_BASE_OFFSET: + # 效果部分成员 + relative_offset = member.offset - PUSH_CONSTANT_BASE_OFFSET + effect_member = MemberInfo( + index=member.index, + name=member.name, + type_id=member.type_id, + offset=relative_offset, + resolved_type=member.resolved_type, + matrix_stride=member.matrix_stride, + array_stride=member.array_stride, + is_row_major=member.is_row_major, + ) + effect_members.append(effect_member) + + # 计算基础部分总大小 + base_total_size = max( + (m.offset + m.size for m in base_members_info), + default=0 + ) + + # 检查是否为标准布局 + is_standard = ( + present_flags == PushConstantBaseMember.STANDARD and + all(m.is_valid for m in base_members_info) + ) + + base_info = PushConstantBaseInfo( + members=base_members_info, + present_flags=present_flags, + total_size=base_total_size, + is_standard_layout=is_standard, + ) + + return PushConstantInfo( + name=struct_name, + struct_type=struct_type, + base_offset=PUSH_CONSTANT_BASE_OFFSET, + base_info=base_info, + effect_members=effect_members, + ) +``` + +--- + +## 4. 代码生成器修改方案 + +### 4.1 新增 Jinja2 模板 + +#### 4.1.1 `push_constant/base_layout.jinja2` + +```jinja2 +{# 生成 Push Constants 基础部分布局信息结构体 #} +/// @brief Push Constants 基础部分布局信息(由着色器工具自动生成) +/// +/// 此结构体描述了着色器声明的 push_constant 基础部分布局, +/// 用于编译期验证与 C++ 端定义的一致性。 +struct {{ struct_name }}_push_constant_base_layout { + // 成员存在性标志 +{%- for member in base_members %} + static constexpr bool has_{{ member.name }} = true; +{%- endfor %} +{%- for expected_name in expected_names %} +{%- if expected_name not in present_names %} + static constexpr bool has_{{ expected_name }} = false; +{%- endif %} +{%- endfor %} + + // 成员偏移量 +{%- for member in base_members %} + static constexpr size_t {{ member.name }}_offset = {{ member.offset }}; +{%- endfor %} + + // 成员大小 +{%- for member in base_members %} + static constexpr size_t {{ member.name }}_size = {{ member.size }}; +{%- endfor %} + + // 总大小 + static constexpr size_t total_size = {{ total_size }}; + + // 布局类型标识 + static constexpr bool is_standard_layout = {{ 'true' if is_standard else 'false' }}; +}; +``` + +#### 4.1.2 `push_constant/static_check.jinja2` + +```jinja2 +{# 生成 Push Constants 静态检查代码 #} +// ============ Push Constants 静态验证 ============ +// 编译期验证着色器声明的基础部分与 C++ 端定义一致 + +{% if is_standard_layout %} +// 验证标准布局 +static_assert( + {{ struct_name }}_push_constant_base_layout::has_projection && + {{ struct_name }}_push_constant_base_layout::projection_offset == + expected_push_constant_base_layout::projection_offset, + "{{ struct_name }}: projection offset mismatch" +); + +static_assert( + {{ struct_name }}_push_constant_base_layout::has_viewport_size && + {{ struct_name }}_push_constant_base_layout::viewport_size_offset == + expected_push_constant_base_layout::viewport_size_offset, + "{{ struct_name }}: viewport_size offset mismatch" +); + +static_assert( + {{ struct_name }}_push_constant_base_layout::has_time && + {{ struct_name }}_push_constant_base_layout::time_offset == + expected_push_constant_base_layout::time_offset, + "{{ struct_name }}: time offset mismatch" +); + +static_assert( + {{ struct_name }}_push_constant_base_layout::total_size >= + expected_push_constant_base_layout::total_size, + "{{ struct_name }}: base layout size too small" +); +{% else %} +// 非标准布局 - 自定义验证 +{%- for member in base_members %} +{%- if not member.is_valid %} +// WARNING: {{ member.name }} layout mismatch +// Expected: offset={{ member.expected_offset }}, size={{ member.expected_size }} +// Actual: offset={{ member.offset }}, size={{ member.size }} +{%- endif %} +{%- endfor %} +{% endif %} +``` + +### 4.2 修改 `code_generator.py` + +添加生成函数: + +```python +def generate_push_constant_base_layout( + push_constant: PushConstantInfo, + shader_name: str +) -> str: + """生成 Push Constants 基础部分布局信息结构体""" + if not push_constant or not push_constant.base_info: + return "" + + base_info = push_constant.base_info + + # 准备模板数据 + base_members = [ + { + 'name': m.name, + 'offset': m.offset, + 'size': m.size, + 'expected_offset': m.expected_offset, + 'expected_size': m.expected_size, + 'is_valid': m.is_valid, + } + for m in base_info.members + ] + + expected_names = ['projection', 'viewport_size', 'time', '_pad0'] + present_names = [m.name for m in base_info.members] + + renderer = get_renderer() + context = { + 'struct_name': shader_name, + 'base_members': base_members, + 'expected_names': expected_names, + 'present_names': present_names, + 'total_size': base_info.total_size, + 'is_standard': base_info.is_standard_layout, + } + + layout_code = renderer.render('push_constant/base_layout.jinja2', context) + check_code = renderer.render('push_constant/static_check.jinja2', context) + + return layout_code + "\n\n" + check_code +``` + +### 4.3 修改头文件模板 + +在 [`header.jinja2`](tools/templates/base/header.jinja2) 中添加: + +```jinja2 +{%- if push_constant_base_layout %} + +// ============ Push Constants 基础部分布局 ============ +// 根据 SPIR-V 反射自动生成 +// 用于编译期验证着色器与 C++ 端定义的一致性 + +{{ push_constant_base_layout }} +{%- endif %} + +{# 在 shader_spec 中添加类型别名 #} +{%- if not is_compute %} +template +struct {{ shader_spec_name }} { + // ... 现有内容 ... + +{%- if has_push_constant_base_layout %} + /// @brief Push Constants 基础部分布局类型 + using push_constant_base_layout = {{ shader_name }}_push_constant_base_layout; +{%- endif %} +}; +{%- endif %} +``` + +--- + +## 5. 静态检查实现方案 + +### 5.1 Concept 定义 + +在新文件 `src/render/push_constant_traits.h` 中: + +```cpp +#pragma once + +#include +#include + +namespace mirage { + +// ============================================================================ +// Push Constant 基础布局 Concept 定义 +// ============================================================================ + +/// @brief 检测类型是否提供了 push_constant_base_layout +template +concept has_push_constant_base_layout = requires { + typename T::push_constant_base_layout; +}; + +/// @brief 验证 push_constant 基础部分是否为标准布局 +/// +/// 标准布局要求: +/// - 包含 projection(mat4,偏移 0) +/// - 包含 viewport_size(vec2,偏移 64) +/// - 包含 time(float,偏移 72) +/// - 总大小至少 80 字节 +template +concept valid_standard_push_constant_base = + Layout::has_projection && + Layout::projection_offset == 0 && + Layout::projection_size == 64 && + Layout::has_viewport_size && + Layout::viewport_size_offset == 64 && + Layout::viewport_size_size == 8 && + Layout::has_time && + Layout::time_offset == 72 && + Layout::time_size == 4 && + Layout::total_size >= 80; + +/// @brief 验证 push_constant 基础部分是否仅包含 projection +/// +/// 用于仅需要投影矩阵的顶点着色器 +template +concept projection_only_push_constant_base = + Layout::has_projection && + Layout::projection_offset == 0 && + Layout::projection_size == 64; + +/// @brief 验证 ShaderSpec 的 push_constant 基础布局是否有效 +/// +/// @tparam ShaderSpec 着色器规格类型 +/// @tparam RequiredLayout Concept 约束(默认为标准布局) +template typename RequiredLayout = valid_standard_push_constant_base> +concept valid_shader_push_constant_base = + has_push_constant_base_layout && + RequiredLayout; + +// ============================================================================ +// 编译期布局验证助手 +// ============================================================================ + +/// @brief 编译期验证 push_constant 布局 +/// +/// 使用方法: +/// @code +/// static_assert(push_constant_layout_validator::validate(), +/// "Push constant base layout mismatch"); +/// @endcode +template +struct push_constant_layout_validator { + static constexpr bool validate() { + if constexpr (!has_push_constant_base_layout) { + return true; // 没有基础布局的着色器无需验证 + } else { + using Layout = typename ShaderSpec::push_constant_base_layout; + return valid_standard_push_constant_base; + } + } + + /// @brief 获取详细的验证错误信息 + static constexpr auto get_validation_errors() { + struct ValidationResult { + bool projection_valid = true; + bool viewport_size_valid = true; + bool time_valid = true; + bool size_valid = true; + }; + + if constexpr (!has_push_constant_base_layout) { + return ValidationResult{}; + } else { + using Layout = typename ShaderSpec::push_constant_base_layout; + return ValidationResult{ + .projection_valid = Layout::has_projection && + Layout::projection_offset == 0, + .viewport_size_valid = Layout::has_viewport_size && + Layout::viewport_size_offset == 64, + .time_valid = Layout::has_time && + Layout::time_offset == 72, + .size_valid = Layout::total_size >= 80, + }; + } + } +}; + +} // namespace mirage +``` + +### 5.2 不同着色器类型的验证策略 + +根据着色器用途,采用不同的验证策略: + +```cpp +namespace mirage { + +// ============================================================================ +// 着色器类型分类 Concepts +// ============================================================================ + +/// @brief Post Effect 着色器要求 +/// 需要完整的标准基础布局 +template +concept post_effect_shader = + has_push_constant_base_layout && + valid_standard_push_constant_base; + +/// @brief Widget 着色器要求 +/// 可能不使用 push_constant 基础部分 +template +concept widget_shader = + !has_push_constant_base_layout || + ShaderSpec::push_constant_base_layout::is_standard_layout; + +/// @brief 仅顶点着色器要求 +/// 只需要 projection 成员 +template +concept vertex_only_shader = + has_push_constant_base_layout && + projection_only_push_constant_base; + +} // namespace mirage +``` + +### 5.3 在 shader_spec_widget 中集成验证 + +修改 [`shader_spec_widget.h`](src/ui/widgets/custom_shader/shader_spec_widget.h) 添加静态验证: + +```cpp +template + requires shader_spec_concept +class shader_spec_widget : public custom_shader_widget_base { +public: + // ... 现有类型定义 ... + + // ======================================================================== + // 静态验证:确保 push_constant 布局正确 + // ======================================================================== + + /// @brief 验证 push_constant 基础布局 + /// 如果 ShaderSpec 提供了 push_constant_base_layout,则验证其布局 + static_assert( + !has_push_constant_base_layout || + push_constant_layout_validator::validate(), + "Push constant base layout does not match expected layout. " + "Check projection (offset 0, size 64), viewport_size (offset 64, size 8), " + "and time (offset 72, size 4)." + ); + + // ... 其余代码不变 ... +}; +``` + +--- + +## 6. 生成代码示例 + +### 6.1 示例着色器:blur.frag.glsl + +基于 [`blur.frag.glsl`](shaders/post_effect/blur.frag.glsl:8) 的 push_constant 定义: + +```glsl +layout(push_constant) uniform PushConstants { + mat4 projection; // 偏移 0, 大小 64 + vec2 viewport_size; // 偏移 64, 大小 8 + float time; // 偏移 72, 大小 4 + float _pad0; // 偏移 76, 大小 4 + vec4 effect_rect; // 偏移 80, 大小 16 + vec4 original_effect_rect; // 偏移 96, 大小 16 + float intensity; // 偏移 112, 大小 4 + float _pad1[3]; // 偏移 116, 大小 12 +} pc; +``` + +### 6.2 生成的头文件示例 + +```cpp +#pragma once + +#include +#include +#include +#include +#include +#include +#include "render/uniform_types.h" + +namespace shaders { + +// ============ Push Constants 基础部分布局 ============ +// 根据 SPIR-V 反射自动生成 +// 用于编译期验证着色器与 C++ 端定义的一致性 + +/// @brief Push Constants 基础部分布局信息(由着色器工具自动生成) +/// +/// 此结构体描述了着色器声明的 push_constant 基础部分布局, +/// 用于编译期验证与 C++ 端定义的一致性。 +struct blur_push_constant_base_layout { + // 成员存在性标志 + static constexpr bool has_projection = true; + static constexpr bool has_viewport_size = true; + static constexpr bool has_time = true; + static constexpr bool has_pad0 = true; + + // 成员偏移量 + static constexpr size_t projection_offset = 0; + static constexpr size_t viewport_size_offset = 64; + static constexpr size_t time_offset = 72; + static constexpr size_t pad0_offset = 76; + + // 成员大小 + static constexpr size_t projection_size = 64; + static constexpr size_t viewport_size_size = 8; + static constexpr size_t time_size = 4; + static constexpr size_t pad0_size = 4; + + // 总大小 + static constexpr size_t total_size = 80; + + // 布局类型标识 + static constexpr bool is_standard_layout = true; +}; + +// ============ Push Constants 静态验证 ============ +// 编译期验证着色器声明的基础部分与 C++ 端定义一致 + +// 验证标准布局 +static_assert( + blur_push_constant_base_layout::has_projection && + blur_push_constant_base_layout::projection_offset == + mirage::expected_push_constant_base_layout::projection_offset, + "blur: projection offset mismatch" +); + +static_assert( + blur_push_constant_base_layout::has_viewport_size && + blur_push_constant_base_layout::viewport_size_offset == + mirage::expected_push_constant_base_layout::viewport_size_offset, + "blur: viewport_size offset mismatch" +); + +static_assert( + blur_push_constant_base_layout::has_time && + blur_push_constant_base_layout::time_offset == + mirage::expected_push_constant_base_layout::time_offset, + "blur: time offset mismatch" +); + +static_assert( + blur_push_constant_base_layout::total_size >= + mirage::expected_push_constant_base_layout::total_size, + "blur: base layout size too small" +); + +// ============ Push Constants 效果部分结构 ============ +// 根据 SPIR-V 反射自动生成 +// 基础部分偏移量:80 字节(由渲染器自动填充) + +// Push Constants 效果部分结构体 +// 偏移量从基础部分结束位置(80字节)开始计算 +struct alignas(16) BlurPushConstantEffect { + // 偏移量:0,大小:16,对齐方式:16 + alignas(16) Eigen::Vector4f effect_rect; + // 偏移量:16,大小:16,对齐方式:16 + alignas(16) Eigen::Vector4f original_effect_rect; + // 偏移量:32,大小:4,对齐方式:4 + alignas(4) float intensity; + // 偏移量:36,大小:12,对齐方式:4 + alignas(4) std::array _pad1; +}; + +// ============ 着色器规格 ============ + +/// @brief blur 着色器规格 +template +struct blur_shader_spec { + using params_type = ParamsT; + + /// @brief 着色器名称 + static constexpr std::string_view name = "blur"; + + /// @brief 着色器唯一标识符(编译时哈希) + static constexpr uint32_t shader_id = /* ... */; + + /// @brief 获取顶点着色器 SPIR-V + [[nodiscard]] static constexpr auto get_vertex_spirv() noexcept + -> std::span; + + /// @brief 获取片段着色器 SPIR-V + [[nodiscard]] static constexpr auto get_fragment_spirv() noexcept + -> std::span; + + /// @brief 获取描述符绑定信息 + [[nodiscard]] static constexpr auto get_bindings() noexcept + -> std::span; + + /// @brief Push Constants 效果部分类型 + using push_constant_effect_type = BlurPushConstantEffect; + + /// @brief Push Constants 基础部分布局类型 + using push_constant_base_layout = blur_push_constant_base_layout; +}; + +} // namespace shaders +``` + +--- + +## 7. 使用示例 + +### 7.1 创建使用标准布局的 Post Effect Widget + +```cpp +#include "shaders/blur_vert_frag.h" // 自动生成的头文件 +#include "ui/widgets/custom_shader/shader_spec_widget.h" + +namespace my_app { + +/// @brief Blur 后处理效果控件 +/// +/// 使用 blur_shader_spec,自动验证 push_constant 布局 +class blur_widget : public mirage::shader_spec_widget> { +public: + blur_widget() { + // 初始化 uniform buffer 参数 + params().radius = 5.0f; + params().samples = 16; + params().direction = Eigen::Vector2f(1.0f, 0.0f); + + // 初始化 push_constant 效果参数 + push_constant_effect().intensity = 1.0f; + push_constant_effect().effect_rect = Eigen::Vector4f(0, 0, 100, 100); + push_constant_effect().original_effect_rect = Eigen::Vector4f(0, 0, 100, 100); + } + + /// @brief 设置模糊强度 + auto& set_intensity(float value) { + push_constant_effect().intensity = value; + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 设置模糊半径 + auto& set_radius(float value) { + params().radius = value; + mark_params_dirty(); + return *this; + } + +protected: + auto get_render_mode() const noexcept + -> mirage::custom_shader_render_mode override { + return mirage::custom_shader_render_mode::post_process; + } +}; + +} // namespace my_app +``` + +### 7.2 编译期错误检测示例 + +当着色器的 push_constant 布局与期望不符时,编译器会报错: + +```cpp +// 假设某个着色器的 push_constant 布局错误: +// projection 偏移量为 16 而非 0 +struct bad_shader_push_constant_base_layout { + static constexpr bool has_projection = true; + static constexpr size_t projection_offset = 16; // 错误! + static constexpr size_t projection_size = 64; + // ... +}; + +// 编译时会产生类似以下错误: +// error: static assertion failed: bad_shader: projection offset mismatch +``` + +### 7.3 验证非标准布局着色器 + +对于使用不同布局的着色器(如 [`mtsdf_text.vert.glsl`](shaders/widget/mtsdf_text.vert.glsl)),可以使用自定义验证: + +```cpp +// 文本着色器有不同的布局(包含 screen_resolution) +// 在生成的头文件中会标记 is_standard_layout = false + +struct mtsdf_text_push_constant_base_layout { + static constexpr bool has_projection = true; + static constexpr bool has_viewport_size = true; + static constexpr bool has_screen_resolution = true; // 非标准成员 + static constexpr bool has_time = true; + + static constexpr size_t projection_offset = 0; + static constexpr size_t viewport_size_offset = 64; + static constexpr size_t screen_resolution_offset = 72; + static constexpr size_t time_offset = 80; + + static constexpr size_t total_size = 88; // 比标准布局大 + static constexpr bool is_standard_layout = false; +}; + +// 使用自定义验证而非标准验证 +static_assert( + mtsdf_text_push_constant_base_layout::has_projection && + mtsdf_text_push_constant_base_layout::projection_offset == 0, + "mtsdf_text: projection must be at offset 0" +); +``` + +### 7.4 使用 Concept 进行条件编译 + +```cpp +template +class adaptive_renderer { +public: + void render() { + if constexpr (mirage::post_effect_shader) { + // 使用完整的 push_constant 基础部分 + render_post_effect(); + } else if constexpr (mirage::vertex_only_shader) { + // 仅使用 projection + render_vertex_only(); + } else { + // 不使用 push_constant 基础部分 + render_widget(); + } + } + +private: + void render_post_effect() { + // 填充完整的 push_constants_base + auto base = mirage::create_push_constants_base(width, height, time); + // ... + } + + void render_vertex_only() { + // 仅填充 projection + float projection[16]; + auto proj = mirage::create_ortho_projection(width, height); + std::memcpy(projection, proj.data(), 64); + // ... + } + + void render_widget() { + // 使用 uniform buffer 而非 push_constant + // ... + } +}; +``` + +--- + +## 8. 实现计划与总结 + +### 8.1 实现步骤 + +```mermaid +flowchart TD + A[步骤 1: 修改 Python 数据结构] --> B[步骤 2: 修改解析器] + B --> C[步骤 3: 创建 Jinja2 模板] + C --> D[步骤 4: 修改代码生成器] + D --> E[步骤 5: 添加 C++ 验证头文件] + E --> F[步骤 6: 更新 uniform_types.h] + F --> G[步骤 7: 测试与验证] + + subgraph Python 端 + A + B + C + D + end + + subgraph C++ 端 + E + F + end + + subgraph 验证 + G + end +``` + +#### 步骤 1: 修改 Python 数据结构 + +**文件**: [`tools/types.py`](tools/types.py) + +- 添加 `PushConstantBaseMember` 枚举 +- 添加 `PushConstantBaseMemberInfo` 数据类 +- 添加 `PushConstantBaseInfo` 数据类 +- 修改 `PushConstantInfo` 添加 `base_info` 字段 + +#### 步骤 2: 修改解析器 + +**文件**: [`tools/spirv_parser.py`](tools/spirv_parser.py) + +- 添加 `_identify_base_member()` 辅助函数 +- 修改 `extract_push_constants()` 提取基础部分信息 + +#### 步骤 3: 创建 Jinja2 模板 + +**新建文件**: +- `tools/templates/push_constant/base_layout.jinja2` +- `tools/templates/push_constant/static_check.jinja2` + +#### 步骤 4: 修改代码生成器 + +**文件**: [`tools/code_generator.py`](tools/code_generator.py) + +- 添加 `generate_push_constant_base_layout()` 函数 +- 修改 `generate_header()` 集成基础布局代码 + +**文件**: [`tools/templates/base/header.jinja2`](tools/templates/base/header.jinja2) + +- 添加基础布局和静态检查代码块 +- 在 `shader_spec` 中添加 `push_constant_base_layout` 类型别名 + +#### 步骤 5: 添加 C++ 验证头文件 + +**新建文件**: `src/render/push_constant_traits.h` + +- 定义 `has_push_constant_base_layout` concept +- 定义 `valid_standard_push_constant_base` concept +- 定义 `push_constant_layout_validator` 模板类 + +#### 步骤 6: 更新 uniform_types.h + +**文件**: [`src/render/uniform_types.h`](src/render/uniform_types.h) + +- 添加 `expected_push_constant_base_layout` 结构体 +- 添加 `push_constants_base` 与期望布局的静态断言 + +#### 步骤 7: 测试与验证 + +- 重新编译所有着色器,验证生成代码正确 +- 故意创建错误布局的着色器,验证静态检查能检测到错误 +- 验证现有控件正常工作 + +### 8.2 文件修改清单 + +| 文件 | 操作 | 描述 | +|-----|------|-----| +| `tools/types.py` | 修改 | 添加新数据结构 | +| `tools/spirv_parser.py` | 修改 | 提取基础部分信息 | +| `tools/code_generator.py` | 修改 | 生成布局代码 | +| `tools/templates/push_constant/base_layout.jinja2` | 新建 | 布局结构体模板 | +| `tools/templates/push_constant/static_check.jinja2` | 新建 | 静态检查模板 | +| `tools/templates/base/header.jinja2` | 修改 | 集成新代码块 | +| `src/render/push_constant_traits.h` | 新建 | C++ Concept 定义 | +| `src/render/uniform_types.h` | 修改 | 添加期望布局 | +| `src/ui/widgets/custom_shader/shader_spec_widget.h` | 修改 | 添加静态验证 | + +### 8.3 向后兼容性 + +本方案完全向后兼容: + +1. **现有着色器无需修改** - 只要布局正确,自动通过验证 +2. **现有控件无需修改** - 验证在编译期自动进行 +3. **非标准布局支持** - 使用 `is_standard_layout = false` 标记,不强制验证 + +### 8.4 总结 + +本设计方案通过以下机制实现 Push Constant 基础部分的存储和验证: + +1. **Python 端**: 解析 SPIR-V,提取基础部分成员信息,生成布局结构体 +2. **C++ 端**: 使用 Concept 和 static_assert 在编译期验证布局一致性 +3. **灵活性**: 支持标准布局和非标准布局,可根据着色器类型选择验证策略 +4. **安全性**: 编译期即可发现布局不匹配问题,避免运行时错误 \ No newline at end of file diff --git a/shaders/widget/blur.frag.glsl b/shaders/widget/blur.frag.glsl deleted file mode 100644 index 8999234..0000000 --- a/shaders/widget/blur.frag.glsl +++ /dev/null @@ -1,92 +0,0 @@ -#version 450 core - -/** - * @brief 高斯模糊效果片段着色器 - * - * 实现单次 pass 的高斯模糊效果。 - * 注意:当前实现采用单次 pass 的简化模糊(非分离式), - * 分离式模糊需要两次渲染 pass(水平+垂直)以获得更好的性能和质量。 - */ - -// ============================================================================ -// Uniform Buffer -// ============================================================================ - -layout(set = 0, binding = 0) uniform BlurParams { - float radius; ///< 模糊半径(像素) - float intensity; ///< 效果强度 [0, 1] - vec2 direction; ///< 模糊方向(用于分离式模糊) - int padding; ///< 对齐填充 -} params; - -// ============================================================================ -// 输入 -// ============================================================================ - -layout(location = 0) in vec2 v_uv; ///< 标准化 UV 坐标 [0, 1] - -// ============================================================================ -// 纹理采样(后处理模式) -// ============================================================================ - -layout(set = 0, binding = 1) uniform sampler2D source_texture; - -// ============================================================================ -// 输出 -// ============================================================================ - -layout(location = 0) out vec4 out_color; ///< 最终颜色输出 - -// ============================================================================ -// 辅助函数 -// ============================================================================ - -/** - * @brief 高斯权重计算 - * @param x 距离中心的偏移量 - * @param sigma 标准差 - * @return 高斯权重值 - */ -float gaussian(float x, float sigma) { - return exp(-(x * x) / (2.0 * sigma * sigma)); -} - -// ============================================================================ -// 主函数 -// ============================================================================ - -void main() { - vec2 uv = v_uv; - - // 获取纹理尺寸和像素大小 - vec2 tex_size = vec2(textureSize(source_texture, 0)); - vec2 texel_size = 1.0 / tex_size; - - // 采样源纹理 - vec4 original = texture(source_texture, uv); - - // 计算高斯模糊 sigma(使用 3σ 原则) - float sigma = params.radius / 3.0; - - // 计算采样范围 - int half_samples = int(params.radius); - - // 限制采样次数以防性能过低 - if (half_samples > 32) half_samples = 32; - - // 单次 pass 简化模糊(仅使用一个方向) - vec4 result = vec4(0.0); - float total_weight = 0.0; - - for (int i = -half_samples; i <= half_samples; ++i) { - float weight = gaussian(float(i), sigma); - vec2 offset = params.direction * float(i) * texel_size; - result += texture(source_texture, uv + offset) * weight; - total_weight += weight; - } - - vec4 blurred = result / total_weight; - - // 根据强度混合原始颜色和模糊效果 - out_color = mix(original, blurred, params.intensity); -} \ No newline at end of file diff --git a/shaders/widget/chromatic_aberration.frag.glsl b/shaders/widget/chromatic_aberration.frag.glsl deleted file mode 100644 index 8e21d68..0000000 --- a/shaders/widget/chromatic_aberration.frag.glsl +++ /dev/null @@ -1,63 +0,0 @@ -#version 450 core - -/** - * @brief 色差效果片段着色器 - * - * 实现 RGB 通道分离偏移效果,模拟透镜色散特性。 - * 用于后处理模式,对源纹理应用效果。 - */ - -// ============================================================================ -// Uniform Buffer -// ============================================================================ - -layout(set = 0, binding = 0) uniform ChromaticAberrationParams { - vec2 offset; ///< RGB 通道偏移量(归一化坐标) - float intensity; ///< 效果强度 [0, 1] - float padding[2]; ///< 对齐填充 -} params; - -// ============================================================================ -// 输入 -// ============================================================================ - -layout(location = 0) in vec2 v_uv; ///< 标准化 UV 坐标 [0, 1] - -// ============================================================================ -// 纹理采样(后处理模式) -// ============================================================================ - -layout(set = 0, binding = 1) uniform sampler2D source_texture; - -// ============================================================================ -// 输出 -// ============================================================================ - -layout(location = 0) out vec4 out_color; ///< 最终颜色输出 - -// ============================================================================ -// 主函数 -// ============================================================================ - -void main() { - vec2 uv = v_uv; - - // 计算从中心到当前像素的方向和距离 - vec2 center = vec2(0.5); - vec2 dir = normalize(uv - center); - float dist = length(uv - center); - - // 计算偏移(从中心向外增强) - vec2 offset_scaled = params.offset * dist * params.intensity; - - // 分离采样 RGB 通道 - // R 通道向外偏移,B 通道向内偏移 - float r = texture(source_texture, uv + dir * offset_scaled).r; - float g = texture(source_texture, uv).g; - float b = texture(source_texture, uv - dir * offset_scaled).b; - - // 保持原始 alpha - float a = texture(source_texture, uv).a; - - out_color = vec4(r, g, b, a); -} \ No newline at end of file diff --git a/shaders/widget/color_adjust.frag.glsl b/shaders/widget/color_adjust.frag.glsl deleted file mode 100644 index f269917..0000000 --- a/shaders/widget/color_adjust.frag.glsl +++ /dev/null @@ -1,65 +0,0 @@ -#version 450 core - -/** - * @brief 颜色调整片段着色器 - * - * 实现亮度、对比度、饱和度和伽马调整效果。 - * 用于后处理模式,对源纹理应用效果。 - */ - -// ============================================================================ -// Uniform Buffer -// ============================================================================ - -layout(set = 0, binding = 0) uniform ColorAdjustParams { - float brightness; ///< 亮度 [-1, 1] - float contrast; ///< 对比度 [0, 2] - float saturation; ///< 饱和度 [0, 2] - float gamma; ///< 伽马 [0.1, 3] -} params; - -// ============================================================================ -// 输入 -// ============================================================================ - -layout(location = 0) in vec2 v_uv; ///< 标准化 UV 坐标 [0, 1] - -// ============================================================================ -// 纹理采样(后处理模式) -// ============================================================================ - -layout(set = 0, binding = 1) uniform sampler2D source_texture; - -// ============================================================================ -// 输出 -// ============================================================================ - -layout(location = 0) out vec4 out_color; ///< 最终颜色输出 - -// ============================================================================ -// 主函数 -// ============================================================================ - -void main() { - vec2 uv = v_uv; - - // 采样源纹理 - vec4 original = texture(source_texture, uv); - vec3 c = original.rgb; - - // 亮度调整 - c += params.brightness; - - // 对比度调整 - c = (c - 0.5) * params.contrast + 0.5; - - // 饱和度调整 - float luminance = dot(c, vec3(0.299, 0.587, 0.114)); - c = mix(vec3(luminance), c, params.saturation); - - // 伽马校正 - c = pow(max(c, vec3(0.0)), vec3(1.0 / params.gamma)); - - // 保持 alpha - out_color = vec4(c, original.a); -} \ No newline at end of file diff --git a/shaders/widget/color_tint.frag.glsl b/shaders/widget/color_tint.frag.glsl deleted file mode 100644 index 9ca2de6..0000000 --- a/shaders/widget/color_tint.frag.glsl +++ /dev/null @@ -1,119 +0,0 @@ -#version 450 core - -/** - * @brief 色调映射片段着色器 - * - * 实现多种混合模式的色调映射效果。 - * 用于后处理模式,对源纹理应用效果。 - */ - -// ============================================================================ -// Uniform Buffer -// ============================================================================ - -layout(set = 0, binding = 0) uniform ColorTintParams { - vec4 tint_color; ///< 色调颜色 [r, g, b, a] - int blend_mode; ///< 混合模式 (0=normal, 1=additive, 2=multiply, 3=screen, 4=overlay) - float intensity; ///< 效果强度 [0, 1] - float padding; ///< 对齐填充 -} params; - -// ============================================================================ -// 输入 -// ============================================================================ - -layout(location = 0) in vec2 v_uv; ///< 标准化 UV 坐标 [0, 1] - -// ============================================================================ -// 纹理采样(后处理模式) -// ============================================================================ - -layout(set = 0, binding = 1) uniform sampler2D source_texture; - -// ============================================================================ -// 输出 -// ============================================================================ - -layout(location = 0) out vec4 out_color; ///< 最终颜色输出 - -// ============================================================================ -// 混合函数 -// ============================================================================ - -/** - * @brief 正常混合 - */ -vec3 blend_normal(vec3 base, vec3 blend) { - return blend; -} - -/** - * @brief 加法混合 - */ -vec3 blend_additive(vec3 base, vec3 blend) { - return base + blend; -} - -/** - * @brief 正片叠底混合 - */ -vec3 blend_multiply(vec3 base, vec3 blend) { - return base * blend; -} - -/** - * @brief 滤色混合 - */ -vec3 blend_screen(vec3 base, vec3 blend) { - return 1.0 - (1.0 - base) * (1.0 - blend); -} - -/** - * @brief 叠加混合 - */ -vec3 blend_overlay(vec3 base, vec3 blend) { - return mix( - 2.0 * base * blend, - 1.0 - 2.0 * (1.0 - base) * (1.0 - blend), - step(0.5, base) - ); -} - -// ============================================================================ -// 主函数 -// ============================================================================ - -void main() { - vec2 uv = v_uv; - - // 采样源纹理 - vec4 original = texture(source_texture, uv); - vec3 result = original.rgb; - vec3 tint_rgb = params.tint_color.rgb; - float tint_alpha = params.tint_color.a * params.intensity; - - // 根据混合模式处理 - switch (params.blend_mode) { - case 0: // Normal - result = mix(result, tint_rgb, tint_alpha); - break; - case 1: // Additive - result = blend_additive(result, tint_rgb * tint_alpha); - break; - case 2: // Multiply - result = mix(result, blend_multiply(result, tint_rgb), tint_alpha); - break; - case 3: // Screen - result = mix(result, blend_screen(result, tint_rgb), tint_alpha); - break; - case 4: // Overlay - result = mix(result, blend_overlay(result, tint_rgb), tint_alpha); - break; - default: - result = mix(result, tint_rgb, tint_alpha); - break; - } - - // 保持 alpha - out_color = vec4(result, original.a); -} \ No newline at end of file diff --git a/shaders/widget/noise.frag.glsl b/shaders/widget/noise.frag.glsl deleted file mode 100644 index 007eda6..0000000 --- a/shaders/widget/noise.frag.glsl +++ /dev/null @@ -1,78 +0,0 @@ -#version 450 core - -/** - * @brief 噪点效果片段着色器 - * - * 实现电影胶片噪点效果,可选择静态或动态(动画)噪点。 - * 用于后处理模式,对源纹理应用效果。 - */ - -// ============================================================================ -// Uniform Buffer -// ============================================================================ - -layout(set = 0, binding = 0) uniform NoiseParams { - float amount; ///< 噪点强度 [0, 1] - float grain_size; ///< 颗粒大小 [0.5, 5.0] - int animated; ///< 是否启用动画 - float time; ///< 时间参数(用于动画) -} params; - -// ============================================================================ -// 输入 -// ============================================================================ - -layout(location = 0) in vec2 v_uv; ///< 标准化 UV 坐标 [0, 1] - -// ============================================================================ -// 纹理采样(后处理模式) -// ============================================================================ - -layout(set = 0, binding = 1) uniform sampler2D source_texture; - -// ============================================================================ -// 输出 -// ============================================================================ - -layout(location = 0) out vec4 out_color; ///< 最终颜色输出 - -// ============================================================================ -// 辅助函数 -// ============================================================================ - -/** - * @brief 简单的伪随机数生成器 - * @param st 二维输入坐标 - * @return 归一化的随机值 [0, 1] - */ -float random(vec2 st) { - return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123); -} - -// ============================================================================ -// 主函数 -// ============================================================================ - -void main() { - vec2 uv = v_uv; - - // 采样源纹理 - vec4 original = texture(source_texture, uv); - - // 计算噪点坐标(根据颗粒大小缩放) - vec2 noise_uv = uv / params.grain_size; - - // 如果启用动画,添加时间偏移使噪点动态变化 - float time_offset = (params.animated != 0) ? params.time : 0.0; - - // 生成噪点 [0, 1] - float noise = random(noise_uv + time_offset); - - // 混合噪点 - 使用叠加模式 - // 噪点值减去 0.5 使其范围变为 [-0.5, 0.5] - vec3 noise_color = vec3(noise); - vec3 result = original.rgb + (noise_color - 0.5) * params.amount; - - // 根据强度混合原始颜色和噪点效果 - out_color = mix(original, vec4(result, original.a), 1.0); -} \ No newline at end of file diff --git a/shaders/widget/vignette.frag.glsl b/shaders/widget/vignette.frag.glsl deleted file mode 100644 index ef1ed9a..0000000 --- a/shaders/widget/vignette.frag.glsl +++ /dev/null @@ -1,62 +0,0 @@ -#version 450 core - -/** - * @brief 暗角效果片段着色器 - * - * 实现屏幕边缘暗角效果,模拟镜头光学特性。 - * 用于后处理模式,对源纹理应用效果。 - */ - -// ============================================================================ -// Uniform Buffer -// ============================================================================ - -layout(set = 0, binding = 0) uniform VignetteParams { - float radius; ///< 暗角半径 [0, 1] - float softness; ///< 边缘柔和度 [0, 1] - float intensity; ///< 效果强度 [0, 1] - float padding; ///< 对齐填充 - vec4 tint_color; ///< 暗角颜色 [r, g, b, a] -} params; - -// ============================================================================ -// 输入 -// ============================================================================ - -layout(location = 0) in vec2 v_uv; ///< 标准化 UV 坐标 [0, 1] - -// ============================================================================ -// 纹理采样(后处理模式) -// ============================================================================ - -layout(set = 0, binding = 1) uniform sampler2D source_texture; - -// ============================================================================ -// 输出 -// ============================================================================ - -layout(location = 0) out vec4 out_color; ///< 最终颜色输出 - -// ============================================================================ -// 主函数 -// ============================================================================ - -void main() { - vec2 uv = v_uv; - - // 采样源纹理 - vec4 original = texture(source_texture, uv); - - // 计算到中心的距离(归一化到 [0, 1]) - vec2 center = vec2(0.5); - float dist = length(uv - center) * 2.0; // [0, sqrt(2)] - - // 计算暗角强度(smoothstep 实现柔和边缘) - float vignette_factor = smoothstep(params.radius, params.radius + params.softness, dist); - - // 应用暗角颜色 - vec4 vignetted = mix(original, params.tint_color, vignette_factor); - - // 根据强度混合原始颜色和暗角效果 - out_color = mix(original, vignetted, params.intensity); -} \ No newline at end of file diff --git a/src/render/push_constant_traits.h b/src/render/push_constant_traits.h new file mode 100644 index 0000000..610a85d --- /dev/null +++ b/src/render/push_constant_traits.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include + +namespace mirage { + /** + * @brief Concept: 验证布局是否包含 projection 成员 + */ + template + concept has_projection_member = requires { + { Layout::has_projection } -> std::convertible_to; + requires Layout::has_projection; + { Layout::projection_offset } -> std::convertible_to; + { Layout::projection_size } -> std::convertible_to; + }; + + /** + * @brief Concept: 验证布局是否包含 viewport_size 成员 + */ + template + concept has_viewport_size_member = requires { + { Layout::has_viewport_size } -> std::convertible_to; + requires Layout::has_viewport_size; + { Layout::viewport_size_offset } -> std::convertible_to; + { Layout::viewport_size_size } -> std::convertible_to; + }; + + /** + * @brief Concept: 验证布局是否包含 time 成员 + */ + template + concept has_time_member = requires { + { Layout::has_time } -> std::convertible_to; + requires Layout::has_time; + { Layout::time_offset } -> std::convertible_to; + { Layout::time_size } -> std::convertible_to; + }; + + /** + * @brief Concept: 验证是否为标准 Push Constant 基础布局 + * + * 标准布局要求: + * - projection: offset=0, size=64 + * - viewport_size: offset=64, size=8 + * - time: offset=72, size=4 + * - total_size=80 + */ + template + concept standard_push_constant_base_layout = + has_projection_member && + has_viewport_size_member && + has_time_member && + requires { + { Layout::is_standard_layout } -> std::convertible_to; + requires Layout::is_standard_layout; + { Layout::total_size } -> std::convertible_to; + requires Layout::total_size == 80; + }; + + /** + * @brief Concept: 仅包含 projection 的布局(用于仅顶点着色器) + */ + template + concept projection_only_layout = + has_projection_member && + requires { + requires Layout::projection_offset == 0; + requires Layout::projection_size == 64; + } && + (!has_viewport_size_member || !Layout::has_viewport_size) && + (!has_time_member || !Layout::has_time); + + /** + * @brief 编译期布局验证器 + */ + template + struct push_constant_layout_validator { + static constexpr bool has_projection = + requires { { Layout::has_projection } -> std::convertible_to; } && Layout::has_projection; + + static constexpr bool has_viewport_size = + requires { { Layout::has_viewport_size } -> std::convertible_to; } && Layout::has_viewport_size; + + static constexpr bool has_time = + requires { { Layout::has_time } -> std::convertible_to; } && Layout::has_time; + + static constexpr bool is_standard = standard_push_constant_base_layout; + + static constexpr bool is_projection_only = projection_only_layout; + + // 验证projection偏移量是否正确(如果存在) + static constexpr bool projection_offset_valid = + !has_projection || Layout::projection_offset == 0; + + // 验证viewport_size偏移量是否正确(如果存在) + static constexpr bool viewport_size_offset_valid = + !has_viewport_size || Layout::viewport_size_offset == 64; + + // 验证time偏移量是否正确(如果存在) + static constexpr bool time_offset_valid = + !has_time || Layout::time_offset == 72; + }; + + /** + * @brief 用于约束需要标准布局的着色器规格 + */ + template + concept shader_with_standard_push_constant = requires { + typename ShaderSpec::push_constant_base_layout; + } && standard_push_constant_base_layout; + + /** + * @brief 用于约束需要projection的着色器规格 + */ + template + concept shader_with_projection = requires { + typename ShaderSpec::push_constant_base_layout; + } && has_projection_member; +} // namespace mirage diff --git a/src/render/render_command.h b/src/render/render_command.h index 35ccabe..f0563d2 100644 --- a/src/render/render_command.h +++ b/src/render/render_command.h @@ -601,70 +601,69 @@ namespace mirage { // 基本信息 // ======================================================================== - uint32_t shader_id; ///< 着色器唯一标识(用于 Pipeline 缓存) - vec2f_t position; ///< 控件位置(左上角) - vec2f_t size; ///< 控件大小 - render_order order; ///< 渲染顺序和裁剪信息 + uint32_t shader_id; ///< 着色器唯一标识(用于 Pipeline 缓存) + vec2f_t position; ///< 控件位置(左上角) + vec2f_t size; ///< 控件大小 + render_order order; ///< 渲染顺序和裁剪信息 // ======================================================================== // 着色器配置 // ======================================================================== - std::span vert_spirv; ///< 顶点着色器 SPIR-V - std::span frag_spirv; ///< 片段着色器 SPIR-V - std::span bindings; ///< 描述符绑定信息 + std::span vert_spirv; ///< 顶点着色器 SPIR-V + std::span frag_spirv; ///< 片段着色器 SPIR-V + std::span bindings; ///< 描述符绑定信息 // ======================================================================== // 渲染模式 // ======================================================================== - ui::custom_shader_render_mode render_mode; ///< 渲染模式 - uint32_t source_texture; ///< 源纹理 ID(post_process 模式) + custom_shader_render_mode render_mode; ///< 渲染模式 + uint32_t source_texture; ///< 源纹理 ID(post_process 模式) // ======================================================================== // 参数数据 // ======================================================================== - const void* params_data; ///< 参数数据指针(UBO 内容) - size_t params_size; ///< 参数数据大小 - bool params_dirty; ///< 参数是否需要更新 + const void* params_data; ///< 参数数据指针(UBO 内容) + size_t params_size; ///< 参数数据大小 + bool params_dirty; ///< 参数是否需要更新 + + // ======================================================================== + // Push Constants 效果参数 + // ======================================================================== + + const void* push_constant_effect_data; ///< 效果参数数据指针(Push Constants 内容) + size_t push_constant_effect_size; ///< 效果参数数据大小(字节) + bool push_constant_effect_dirty; ///< 效果参数是否需要更新 // ======================================================================== // 顶点数据(custom_geometry 模式) // ======================================================================== - std::optional vertex_config; ///< 顶点缓冲配置 - std::optional index_config; ///< 索引缓冲配置 - bool vertices_dirty; ///< 顶点是否需要更新 - - // ======================================================================== - // 子控件渲染(post_process 模式) - // ======================================================================== - - /// @brief 子控件渲染命令(当 render_mode == post_process 且无源纹理时) - /// @note 渲染器会先将子控件渲染到临时目标,再应用着色器 - std::vector child_commands; - aabb2d_t child_bounds; + std::optional vertex_config; ///< 顶点缓冲配置 + std::optional index_config; ///< 索引缓冲配置 + bool vertices_dirty; ///< 顶点是否需要更新 /// @brief 默认构造函数 - custom_shader_widget_command() - : shader_id(0) - , position(vec2f_t::Zero()) - , size(vec2f_t::Zero()) - , order() - , vert_spirv() - , frag_spirv() - , bindings() - , render_mode(ui::custom_shader_render_mode::procedural) - , source_texture(0) - , params_data(nullptr) - , params_size(0) - , params_dirty(true) - , vertex_config(std::nullopt) - , index_config(std::nullopt) - , vertices_dirty(false) - , child_commands() - , child_bounds(aabb2d_t::Zero()) { + custom_shader_widget_command() : shader_id(0) + , position(vec2f_t::Zero()) + , size(vec2f_t::Zero()) + , order() + , vert_spirv() + , frag_spirv() + , bindings() + , render_mode(custom_shader_render_mode::procedural) + , source_texture(0) + , params_data(nullptr) + , params_size(0) + , params_dirty(true) + , push_constant_effect_data(nullptr) + , push_constant_effect_size(0) + , push_constant_effect_dirty(false) + , vertex_config(std::nullopt) + , index_config(std::nullopt) + , vertices_dirty(false) { } }; @@ -678,6 +677,6 @@ namespace mirage { post_effect_command_t, mask_begin_command, mask_end_command, - custom_shader_widget_command // 新增 + custom_shader_widget_command // 新增 >; } // namespace mirage diff --git a/src/render/renderers/custom_shader_widget_renderer.cpp b/src/render/renderers/custom_shader_widget_renderer.cpp index 64f8694..a35067b 100644 --- a/src/render/renderers/custom_shader_widget_renderer.cpp +++ b/src/render/renderers/custom_shader_widget_renderer.cpp @@ -10,582 +10,615 @@ #include "render/uniform_types.h" #include "ui/widgets/custom_shader/custom_shader_widget_types.h" +#include #include #include #include #include +#include // TODO: 引入编译后的 SPIR-V 头文件 // #include "custom_shader_quad_vert_spirv.h" // #include "custom_shader_quad_procedural_vert_spirv.h" namespace mirage { + // ============================================================================ + // 构造与析构 + // ============================================================================ + + custom_shader_widget_renderer::custom_shader_widget_renderer( + logical_device& device, + resource_manager& res_mgr, + shader_resource_manager& shader_res_mgr + ) : device_(device) + , res_mgr_(res_mgr) + , shader_res_mgr_(shader_res_mgr) + , target_pool_(nullptr) + , render_pass_(VK_NULL_HANDLE) { + } + + custom_shader_widget_renderer::~custom_shader_widget_renderer() { + cleanup(); + } + + // ============================================================================ + // 初始化 + // ============================================================================ + + void custom_shader_widget_renderer::initialize(vk::RenderPass render_pass) { + render_pass_ = render_pass; + + // 创建程序化四边形顶点缓冲 + create_procedural_quad_buffer(); + } + + void custom_shader_widget_renderer::create_procedural_quad_buffer() { + // 创建默认四边形顶点数据(6个顶点,2个三角形) + // 三角形顺序: (0,1,2), (1,3,2) + std::array quad_vertices = { + { + // 左上三角形 + { + {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f} + }, // 顶点0 + { + {1.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f} + }, // 顶点1 + { + {0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f} + }, // 顶点2 + // 右下三角形 + { + {1.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f} + }, // 顶点3 + { + {1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f} + }, // 顶点4 + { + {0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f} + } // 顶点5 + } + }; + + // 创建索引数据 + std::array quad_indices = {0, 1, 2, 2, 1, 3}; + + auto device_handle = device_.get_handle(); + + // 创建顶点 Buffer + VkBufferCreateInfo vertex_buffer_info{}; + vertex_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + vertex_buffer_info.size = sizeof(quad_vertices); + vertex_buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + vertex_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VkBuffer vertex_buffer; + if (vkCreateBuffer(device_handle, &vertex_buffer_info, nullptr, &vertex_buffer) != VK_SUCCESS) { + throw std::runtime_error("Failed to create procedural quad vertex buffer"); + } + + VkMemoryRequirements vertex_requirements; + vkGetBufferMemoryRequirements(device_handle, vertex_buffer, &vertex_requirements); + + VkMemoryAllocateInfo vertex_alloc_info{}; + vertex_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + vertex_alloc_info.allocationSize = vertex_requirements.size; + vertex_alloc_info.memoryTypeIndex = res_mgr_.find_memory_type( + vertex_requirements.memoryTypeBits, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent + ); + + VkDeviceMemory vertex_memory; + if (vkAllocateMemory(device_handle, &vertex_alloc_info, nullptr, &vertex_memory) != VK_SUCCESS) { + vkDestroyBuffer(device_handle, vertex_buffer, nullptr); + throw std::runtime_error("Failed to allocate vertex buffer memory"); + } + + vkBindBufferMemory(device_handle, vertex_buffer, vertex_memory, 0); + + // 创建索引 Buffer + VkBufferCreateInfo index_buffer_info{}; + index_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + index_buffer_info.size = sizeof(quad_indices); + index_buffer_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + index_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VkBuffer index_buffer; + if (vkCreateBuffer(device_handle, &index_buffer_info, nullptr, &index_buffer) != VK_SUCCESS) { + vkDestroyBuffer(device_handle, vertex_buffer, nullptr); + vkFreeMemory(device_handle, vertex_memory, nullptr); + throw std::runtime_error("Failed to create procedural quad index buffer"); + } + + VkMemoryRequirements index_requirements; + vkGetBufferMemoryRequirements(device_handle, index_buffer, &index_requirements); + + VkMemoryAllocateInfo index_alloc_info{}; + index_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + index_alloc_info.allocationSize = index_requirements.size; + index_alloc_info.memoryTypeIndex = res_mgr_.find_memory_type( + index_requirements.memoryTypeBits, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent + ); + + VkDeviceMemory index_memory; + if (vkAllocateMemory(device_handle, &index_alloc_info, nullptr, &index_memory) != VK_SUCCESS) { + vkDestroyBuffer(device_handle, vertex_buffer, nullptr); + vkFreeMemory(device_handle, vertex_memory, nullptr); + vkDestroyBuffer(device_handle, index_buffer, nullptr); + throw std::runtime_error("Failed to allocate index buffer memory"); + } + + vkBindBufferMemory(device_handle, index_buffer, index_memory, 0); + + // 填充数据 + void* vertex_data_ptr; + vkMapMemory(device_handle, vertex_memory, 0, sizeof(quad_vertices), 0, &vertex_data_ptr); + std::memcpy(vertex_data_ptr, quad_vertices.data(), sizeof(quad_vertices)); + vkUnmapMemory(device_handle, vertex_memory); + + void* index_data_ptr; + vkMapMemory(device_handle, index_memory, 0, sizeof(quad_indices), 0, &index_data_ptr); + std::memcpy(index_data_ptr, quad_indices.data(), sizeof(quad_indices)); + vkUnmapMemory(device_handle, index_memory); + + // 创建 RAII 句柄 + procedural_quad_buffer_ = device_handle_t( + vertex_buffer, + device_deleter{device_handle} + ); + procedural_quad_index_buffer_ = device_handle_t( + index_buffer, + device_deleter{device_handle} + ); + } + + void custom_shader_widget_renderer::set_viewport(const vec2f_t& viewport_size) { + viewport_size_ = viewport_size; + } + + void custom_shader_widget_renderer::set_target_pool(unified_target_pool* pool) { + target_pool_ = pool; + } + + // ============================================================================ + // 渲染入口 + // ============================================================================ + + void custom_shader_widget_renderer::render_impl( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + custom_shader_buffers* buffers, + vk::DescriptorPool descriptor_pool + ) { + // 参数有效性检查 + if (command.size.x() <= 0 || command.size.y() <= 0) { + return; + } + + // 设置 Stencil 参考值 + cmd.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, stencil_ref_); + + // 根据渲染模式分发 + switch (command.render_mode) { + case custom_shader_render_mode::procedural: + render_procedural(cmd, command, descriptor_pool); + break; + + case custom_shader_render_mode::post_process: + render_post_process(cmd, command, buffers, descriptor_pool); + break; + + case custom_shader_render_mode::custom_geometry: + render_custom_geometry(cmd, command, descriptor_pool); + break; + + default: + // 未知的渲染模式,跳过 + break; + } + } + + // ============================================================================ + // 程序化渲染模式 + // ============================================================================ + + void custom_shader_widget_renderer::render_procedural( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + vk::DescriptorPool descriptor_pool + ) { + // 获取或创建 Pipeline + auto vert_spirv = command.vert_spirv.empty() ? procedural_vert_spirv_ : command.vert_spirv; + auto frag_spirv = command.frag_spirv; + + if (vert_spirv.empty() || frag_spirv.empty()) { + return; // 无效的着色器 + } + + // 计算顶点布局哈希(程序化模式使用空的顶点输入) + auto vertex_layout_hash = shader_resource_manager::compute_vertex_layout_hash({}, {}); + + pipeline_key pipeline_key{ + .shader_id = command.shader_id, + .vertex_format_hash = vertex_layout_hash, + .render_pass = render_pass_ + }; + + auto pipeline = shader_res_mgr_.get_or_create_pipeline( + pipeline_key, + vert_spirv, + frag_spirv, + command.bindings, + {}, + {}, + render_pass_ + ); + + if (!pipeline) { + return; // Pipeline 创建失败 + } + + // 获取 Pipeline Layout + auto desc_layout = shader_res_mgr_.get_or_create_descriptor_set_layout(command.bindings); + + // 分配描述符集 + auto desc_set = shader_res_mgr_.allocate_descriptor_set(desc_layout); + if (!desc_set) { + return; // 描述符集分配失败 + } + + // 更新 Uniform Buffer 描述符(如果有参数) + if (command.params_data && command.params_size > 0) { + auto allocation = shader_res_mgr_.allocate_uniform_buffer(command.params_size); + if (allocation.mapped_ptr) { + std::memcpy(allocation.mapped_ptr, command.params_data, command.params_size); + + // 更新描述符 + for (const auto& binding : command.bindings) { + if (binding.type == vk::DescriptorType::eUniformBuffer) { + shader_res_mgr_.update_uniform_buffer_descriptor( + desc_set, + binding.binding, + allocation.buffer, + allocation.offset, + allocation.size + ); + } + } + } + } + + // 绑定 Pipeline + auto pipeline_layout = shader_res_mgr_.get_pipeline_layout(); + cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); + + // 绑定描述符集 + cmd.bindDescriptorSets( + vk::PipelineBindPoint::eGraphics, + pipeline_layout, + 0, 1, &desc_set, + 0, nullptr + ); + + // 设置 Push Constants(程序化模式使用专用的 push constant) + procedural_quad_push_constants pc{}; + pc.rect = vec4f_t( + command.position.x(), + command.position.y(), + command.size.x(), + command.size.y() + ); + pc.color = vec4f_t(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色 + + cmd.pushConstants( + pipeline_layout, + vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, + 0, + sizeof(procedural_quad_push_constants), + &pc + ); + + // 设置视口和裁剪 + vk::Viewport viewport{0.0f, 0.0f, viewport_size_.x(), viewport_size_.y(), 0.0f, 1.0f}; + cmd.setViewport(0, 1, &viewport); + + vk::Rect2D scissor{ + {0, 0}, {static_cast(viewport_size_.x()), static_cast(viewport_size_.y())} + }; + cmd.setScissor(0, 1, &scissor); + + // 绘制(6个顶点,2个三角形) + cmd.draw(6, 1, 0, 0); + } + + // ============================================================================ + // 后处理渲染模式 + // ============================================================================ + + void custom_shader_widget_renderer::render_post_process( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + custom_shader_buffers* buffers, + vk::DescriptorPool descriptor_pool + ) { + // TODO: 后处理模式实现 + // 1. 如果有子控件,先渲染到临时目标 + // 2. 获取或创建 Pipeline(使用标准顶点着色器) + // 3. 绑定源纹理描述符 + // 4. 绑定参数 UBO + // 5. 渲染四边形 + + // 目前简化实现:使用标准四边形渲染 + render_procedural(cmd, command, descriptor_pool); + } + + // ============================================================================ + // 自定义几何渲染模式 + // ============================================================================ + + void custom_shader_widget_renderer::render_custom_geometry( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + vk::DescriptorPool descriptor_pool + ) { + // 获取或创建 Pipeline(使用命令中的顶点着色器) + auto vert_spirv = command.vert_spirv.empty() ? default_vert_spirv_ : command.vert_spirv; + auto frag_spirv = command.frag_spirv; + + if (vert_spirv.empty() || frag_spirv.empty()) { + return; + } + + // 检查是否有自定义顶点配置 + if (!command.vertex_config.has_value()) { + return; // 无效的自定义几何配置 + } + + const auto& vertex_config = command.vertex_config.value(); + + // 计算顶点布局哈希 + std::vector bindings; + std::vector attributes; + + bindings.push_back(vertex_config.binding); + for (const auto& attr : vertex_config.attributes) { + attributes.push_back(attr); + } + + auto vertex_layout_hash = shader_resource_manager::compute_vertex_layout_hash( + std::span(bindings), + std::span(attributes) + ); + + pipeline_key pipeline_key{ + .shader_id = command.shader_id, + .vertex_format_hash = vertex_layout_hash, + .render_pass = render_pass_ + }; + + auto pipeline = shader_res_mgr_.get_or_create_pipeline( + pipeline_key, + vert_spirv, + frag_spirv, + command.bindings, + std::span(bindings), + std::span(attributes), + render_pass_ + ); + + if (!pipeline) { + return; + } + + // 获取 Pipeline Layout + auto desc_layout = shader_res_mgr_.get_or_create_descriptor_set_layout(command.bindings); + auto pipeline_layout = shader_res_mgr_.get_pipeline_layout(); + + // 分配描述符集 + auto desc_set = shader_res_mgr_.allocate_descriptor_set(desc_layout); + if (!desc_set) { + return; + } + + // 更新 Uniform Buffer 描述符 + if (command.params_data && command.params_size > 0) { + auto allocation = shader_res_mgr_.allocate_uniform_buffer(command.params_size); + if (allocation.mapped_ptr) { + std::memcpy(allocation.mapped_ptr, command.params_data, command.params_size); + + for (const auto& binding : command.bindings) { + if (binding.type == vk::DescriptorType::eUniformBuffer) { + shader_res_mgr_.update_uniform_buffer_descriptor( + desc_set, + binding.binding, + allocation.buffer, + allocation.offset, + allocation.size + ); + } + } + } + } + + // 绑定 Pipeline + cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); + + // 绑定描述符集 + cmd.bindDescriptorSets( + vk::PipelineBindPoint::eGraphics, + pipeline_layout, + 0, 1, &desc_set, + 0, nullptr + ); + + // 设置视口和裁剪 + vk::Viewport viewport{0.0f, 0.0f, viewport_size_.x(), viewport_size_.y(), 0.0f, 1.0f}; + cmd.setViewport(0, 1, &viewport); + + vk::Rect2D scissor{ + {0, 0}, {static_cast(viewport_size_.x()), static_cast(viewport_size_.y())} + }; + cmd.setScissor(0, 1, &scissor); + + // 上传顶点数据到 GPU + vk::Buffer vertex_buffer = VK_NULL_HANDLE; + VkDeviceSize vertex_offset = 0; + + if (command.vertex_config.has_value()) { + const auto& vc = command.vertex_config.value(); + + // 分配顶点 Buffer + vertex_buffer = shader_res_mgr_.allocate_vertex_buffer(vc.data_size); + if (vertex_buffer) { + // 映射并复制数据 + VkMemoryRequirements mem_requirements; + vkGetBufferMemoryRequirements(device_.get_handle(), vertex_buffer, &mem_requirements); + + VkMemoryAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_requirements.size; + alloc_info.memoryTypeIndex = res_mgr_.find_memory_type( + mem_requirements.memoryTypeBits, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent + ); + + VkDeviceMemory memory; + vkAllocateMemory(device_.get_handle(), &alloc_info, nullptr, &memory); + vkBindBufferMemory(device_.get_handle(), vertex_buffer, memory, 0); + + void* data_ptr; + vkMapMemory(device_.get_handle(), memory, 0, vc.data_size, 0, &data_ptr); + std::memcpy(data_ptr, vc.data, vc.data_size); + vkUnmapMemory(device_.get_handle(), memory); + + vertex_offset = 0; + } + } + + // 绑定顶点缓冲 + if (vertex_buffer) { + cmd.bindVertexBuffers(0, 1, &vertex_buffer, &vertex_offset); + } + + // 处理索引缓冲 + if (command.index_config.has_value()) { + const auto& ic = command.index_config.value(); + + // 分配索引 Buffer + auto index_buffer = shader_res_mgr_.allocate_index_buffer(ic.index_count * sizeof(uint32_t)); + if (index_buffer) { + // 映射并复制数据 + VkMemoryRequirements mem_requirements; + vkGetBufferMemoryRequirements(device_.get_handle(), index_buffer, &mem_requirements); + + VkMemoryAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_requirements.size; + alloc_info.memoryTypeIndex = res_mgr_.find_memory_type( + mem_requirements.memoryTypeBits, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent + ); + + VkDeviceMemory memory; + vkAllocateMemory(device_.get_handle(), &alloc_info, nullptr, &memory); + vkBindBufferMemory(device_.get_handle(), index_buffer, memory, 0); + + void* data_ptr; + vkMapMemory(device_.get_handle(), memory, 0, ic.index_count * sizeof(uint32_t), 0, &data_ptr); + std::memcpy(data_ptr, ic.data, ic.index_count * sizeof(uint32_t)); + vkUnmapMemory(device_.get_handle(), memory); + + // 绑定索引缓冲 + cmd.bindIndexBuffer(index_buffer, 0, vk::IndexType::eUint32); + + // 绘制索引几何 + cmd.drawIndexed( + static_cast(ic.index_count), + 1, + 0, + 0, + 0 + ); + } + } + else { + // 无索引缓冲,直接绘制 + uint32_t vertex_count = command.vertex_config.has_value() + ? static_cast(command.vertex_config.value().vertex_count) + : 0; + + if (vertex_count > 0) { + cmd.draw(vertex_count, 1, 0, 0); + } + } + } + + // ============================================================================ + // 辅助方法 + // ============================================================================ + + auto custom_shader_widget_renderer::setup_pipeline_and_descriptors( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + vk::DescriptorPool descriptor_pool, + vk::ImageView source_texture_view + ) -> std::tuple { + // TODO: 实现完整的 Pipeline 和描述符设置 + return {VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE}; + } + + void custom_shader_widget_renderer::push_constants( + vk::CommandBuffer cmd, + vk::PipelineLayout layout, + const custom_shader_widget_command& command + ) { + // 计算当前时间 + static auto start_time = std::chrono::high_resolution_clock::now(); + auto current_time = std::chrono::high_resolution_clock::now(); + float time = std::chrono::duration(current_time - start_time).count(); + + // 1. 创建并推送基础部分(偏移 0,大小 80 字节) + auto base = create_push_constants_base(viewport_size_.x(), viewport_size_.y(), time); + + cmd.pushConstants( + layout, + vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, + 0, // 偏移量:0 + sizeof(push_constants_base), // 大小:80 字节 + &base + ); + + // 2. 推送效果部分(偏移 80,大小由 command 提供) + if (command.push_constant_effect_data && command.push_constant_effect_size > 0) { + cmd.pushConstants( + layout, + vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, + PUSH_CONSTANT_BASE_OFFSET, // 偏移量:80 + command.push_constant_effect_size, // 大小:由 widget 提供 + command.push_constant_effect_data + ); + } + } + + void custom_shader_widget_renderer::set_stencil_ref(uint8_t ref) { + stencil_ref_ = ref; + } + + auto custom_shader_widget_renderer::get_stencil_ref() const -> uint8_t { + return stencil_ref_; + } + + void custom_shader_widget_renderer::cleanup() { + // RAII 句柄自动管理资源销毁 + procedural_quad_buffer_.reset(); + procedural_quad_index_buffer_.reset(); + } +} // namespace mirage -// ============================================================================ -// 构造与析构 -// ============================================================================ - -custom_shader_widget_renderer::custom_shader_widget_renderer( - logical_device& device, - resource_manager& res_mgr, - shader_resource_manager& shader_res_mgr -) : device_(device) - , res_mgr_(res_mgr) - , shader_res_mgr_(shader_res_mgr) - , target_pool_(nullptr) - , render_pass_(VK_NULL_HANDLE) { -} - -custom_shader_widget_renderer::~custom_shader_widget_renderer() { - cleanup(); -} - -// ============================================================================ -// 初始化 -// ============================================================================ - -void custom_shader_widget_renderer::initialize(vk::RenderPass render_pass) { - render_pass_ = render_pass; - - // 创建程序化四边形顶点缓冲 - create_procedural_quad_buffer(); -} - -void custom_shader_widget_renderer::create_procedural_quad_buffer() { - // 创建默认四边形顶点数据(6个顶点,2个三角形) - // 三角形顺序: (0,1,2), (1,3,2) - std::array quad_vertices = {{ - // 左上三角形 - {{0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}, // 顶点0 - {{1.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}, // 顶点1 - {{0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}, // 顶点2 - // 右下三角形 - {{1.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}, // 顶点3 - {{1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}, // 顶点4 - {{0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}} // 顶点5 - }}; - - // 创建索引数据 - std::array quad_indices = {0, 1, 2, 2, 1, 3}; - - auto device_handle = device_.get_handle(); - - // 创建顶点 Buffer - VkBufferCreateInfo vertex_buffer_info{}; - vertex_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - vertex_buffer_info.size = sizeof(quad_vertices); - vertex_buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - vertex_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VkBuffer vertex_buffer; - if (vkCreateBuffer(device_handle, &vertex_buffer_info, nullptr, &vertex_buffer) != VK_SUCCESS) { - throw std::runtime_error("Failed to create procedural quad vertex buffer"); - } - - VkMemoryRequirements vertex_requirements; - vkGetBufferMemoryRequirements(device_handle, vertex_buffer, &vertex_requirements); - - VkMemoryAllocateInfo vertex_alloc_info{}; - vertex_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - vertex_alloc_info.allocationSize = vertex_requirements.size; - vertex_alloc_info.memoryTypeIndex = res_mgr_.find_memory_type( - vertex_requirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE | VK_MEMORY_PROPERTY_HOST_COHERENT - ); - - VkDeviceMemory vertex_memory; - if (vkAllocateMemory(device_handle, &vertex_alloc_info, nullptr, &vertex_memory) != VK_SUCCESS) { - vkDestroyBuffer(device_handle, vertex_buffer, nullptr); - throw std::runtime_error("Failed to allocate vertex buffer memory"); - } - - vkBindBufferMemory(device_handle, vertex_buffer, vertex_memory, 0); - - // 创建索引 Buffer - VkBufferCreateInfo index_buffer_info{}; - index_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - index_buffer_info.size = sizeof(quad_indices); - index_buffer_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - index_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VkBuffer index_buffer; - if (vkCreateBuffer(device_handle, &index_buffer_info, nullptr, &index_buffer) != VK_SUCCESS) { - vkDestroyBuffer(device_handle, vertex_buffer, nullptr); - vkFreeMemory(device_handle, vertex_memory, nullptr); - throw std::runtime_error("Failed to create procedural quad index buffer"); - } - - VkMemoryRequirements index_requirements; - vkGetBufferMemoryRequirements(device_handle, index_buffer, &index_requirements); - - VkMemoryAllocateInfo index_alloc_info{}; - index_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - index_alloc_info.allocationSize = index_requirements.size; - index_alloc_info.memoryTypeIndex = res_mgr_.find_memory_type( - index_requirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE | VK_MEMORY_PROPERTY_HOST_COHERENT - ); - - VkDeviceMemory index_memory; - if (vkAllocateMemory(device_handle, &index_alloc_info, nullptr, &index_memory) != VK_SUCCESS) { - vkDestroyBuffer(device_handle, vertex_buffer, nullptr); - vkFreeMemory(device_handle, vertex_memory, nullptr); - vkDestroyBuffer(device_handle, index_buffer, nullptr); - throw std::runtime_error("Failed to allocate index buffer memory"); - } - - vkBindBufferMemory(device_handle, index_buffer, index_memory, 0); - - // 填充数据 - void* vertex_data_ptr; - vkMapMemory(device_handle, vertex_memory, 0, sizeof(quad_vertices), 0, &vertex_data_ptr); - std::memcpy(vertex_data_ptr, quad_vertices.data(), sizeof(quad_vertices)); - vkUnmapMemory(device_handle, vertex_memory); - - void* index_data_ptr; - vkMapMemory(device_handle, index_memory, 0, sizeof(quad_indices), 0, &index_data_ptr); - std::memcpy(index_data_ptr, quad_indices.data(), sizeof(quad_indices)); - vkUnmapMemory(device_handle, index_memory); - - // 创建 RAII 句柄 - procedural_quad_buffer_ = device_handle_t( - vertex_buffer, - device_deleter{device_handle} - ); - procedural_quad_index_buffer_ = device_handle_t( - index_buffer, - device_deleter{device_handle} - ); -} - -void custom_shader_widget_renderer::set_viewport(const vec2f_t& viewport_size) { - viewport_size_ = viewport_size; -} - -void custom_shader_widget_renderer::set_target_pool(unified_target_pool* pool) { - target_pool_ = pool; -} - -// ============================================================================ -// 渲染入口 -// ============================================================================ - -void custom_shader_widget_renderer::render_impl( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - custom_shader_buffers* buffers, - vk::DescriptorPool descriptor_pool -) { - // 参数有效性检查 - if (command.size.x() <= 0 || command.size.y() <= 0) { - return; - } - - // 设置 Stencil 参考值 - cmd.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, stencil_ref_); - - // 根据渲染模式分发 - switch (command.render_mode) { - case ui::custom_shader_render_mode::procedural: - render_procedural(cmd, command, descriptor_pool); - break; - - case ui::custom_shader_render_mode::post_process: - render_post_process(cmd, command, buffers, descriptor_pool); - break; - - case ui::custom_shader_render_mode::custom_geometry: - render_custom_geometry(cmd, command, descriptor_pool); - break; - - default: - // 未知的渲染模式,跳过 - break; - } -} - -// ============================================================================ -// 程序化渲染模式 -// ============================================================================ - -void custom_shader_widget_renderer::render_procedural( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - vk::DescriptorPool descriptor_pool -) { - // 获取或创建 Pipeline - auto vert_spirv = command.vert_spirv.empty() ? procedural_vert_spirv_ : command.vert_spirv; - auto frag_spirv = command.frag_spirv; - - if (vert_spirv.empty() || frag_spirv.empty()) { - return; // 无效的着色器 - } - - // 计算顶点布局哈希(程序化模式使用空的顶点输入) - auto vertex_layout_hash = mirage::shader_resource_manager::compute_vertex_layout_hash({}, {}); - - pipeline_key pipeline_key{ - .shader_id = command.shader_id, - .vertex_format_hash = vertex_layout_hash, - .render_pass = render_pass_ - }; - - auto pipeline = shader_res_mgr_.get_or_create_pipeline( - pipeline_key, - vert_spirv, - frag_spirv, - command.bindings, - {}, - {}, - render_pass_ - ); - - if (!pipeline) { - return; // Pipeline 创建失败 - } - - // 获取 Pipeline Layout - auto desc_layout = shader_res_mgr_.get_or_create_descriptor_set_layout(command.bindings); - - // 分配描述符集 - auto desc_set = shader_res_mgr_.allocate_descriptor_set(desc_layout); - if (!desc_set) { - return; // 描述符集分配失败 - } - - // 更新 Uniform Buffer 描述符(如果有参数) - if (command.params_data && command.params_size > 0) { - auto allocation = shader_res_mgr_.allocate_uniform_buffer(command.params_size); - if (allocation.mapped_ptr) { - std::memcpy(allocation.mapped_ptr, command.params_data, command.params_size); - - // 更新描述符 - for (const auto& binding : command.bindings) { - if (binding.type == vk::DescriptorType::eUniformBuffer) { - shader_res_mgr_.update_uniform_buffer_descriptor( - desc_set, - binding.binding, - allocation.buffer, - allocation.offset, - allocation.size - ); - } - } - } - } - - // 绑定 Pipeline - auto pipeline_layout = shader_res_mgr_.get_pipeline_layout(); - cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - - // 绑定描述符集 - cmd.bindDescriptorSets( - vk::PipelineBindPoint::eGraphics, - pipeline_layout, - 0, 1, &desc_set, - 0, nullptr - ); - - // 设置 Push Constants(程序化模式使用专用的 push constant) - procedural_quad_push_constants pc{}; - pc.rect = glm::vec4( - command.position.x(), - command.position.y(), - command.size.x(), - command.size.y() - ); - pc.color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色 - pc.corner_radius = 0.0f; - pc.padding[0] = 0.0f; - pc.padding[1] = 0.0f; - pc.padding[2] = 0.0f; - - cmd.pushConstants( - pipeline_layout, - vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, - 0, - sizeof(procedural_quad_push_constants), - &pc - ); - - // 设置视口和裁剪 - vk::Viewport viewport{0.0f, 0.0f, viewport_size_.x(), viewport_size_.y(), 0.0f, 1.0f}; - cmd.setViewport(0, 1, &viewport); - - vk::Rect2D scissor{{0, 0}, {static_cast(viewport_size_.x()), static_cast(viewport_size_.y())}}; - cmd.setScissor(0, 1, &scissor); - - // 绘制(6个顶点,2个三角形) - cmd.draw(6, 1, 0, 0); -} - -// ============================================================================ -// 后处理渲染模式 -// ============================================================================ - -void custom_shader_widget_renderer::render_post_process( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - custom_shader_buffers* buffers, - vk::DescriptorPool descriptor_pool -) { - // TODO: 后处理模式实现 - // 1. 如果有子控件,先渲染到临时目标 - // 2. 获取或创建 Pipeline(使用标准顶点着色器) - // 3. 绑定源纹理描述符 - // 4. 绑定参数 UBO - // 5. 渲染四边形 - - // 目前简化实现:使用标准四边形渲染 - render_procedural(cmd, command, descriptor_pool); -} - -// ============================================================================ -// 自定义几何渲染模式 -// ============================================================================ - -void custom_shader_widget_renderer::render_custom_geometry( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - vk::DescriptorPool descriptor_pool -) { - // 获取或创建 Pipeline(使用命令中的顶点着色器) - auto vert_spirv = command.vert_spirv.empty() ? default_vert_spirv_ : command.vert_spirv; - auto frag_spirv = command.frag_spirv; - - if (vert_spirv.empty() || frag_spirv.empty()) { - return; - } - - // 检查是否有自定义顶点配置 - if (!command.vertex_config.has_value()) { - return; // 无效的自定义几何配置 - } - - const auto& vertex_config = command.vertex_config.value(); - - // 计算顶点布局哈希 - std::vector bindings; - std::vector attributes; - - bindings.push_back(vertex_config.binding); - for (const auto& attr : vertex_config.attributes) { - attributes.push_back(attr); - } - - auto vertex_layout_hash = mirage::shader_resource_manager::compute_vertex_layout_hash( - std::span(bindings), - std::span(attributes) - ); - - pipeline_key pipeline_key{ - .shader_id = command.shader_id, - .vertex_format_hash = vertex_layout_hash, - .render_pass = render_pass_ - }; - - auto pipeline = shader_res_mgr_.get_or_create_pipeline( - pipeline_key, - vert_spirv, - frag_spirv, - command.bindings, - std::span(bindings), - std::span(attributes), - render_pass_ - ); - - if (!pipeline) { - return; - } - - // 获取 Pipeline Layout - auto desc_layout = shader_res_mgr_.get_or_create_descriptor_set_layout(command.bindings); - auto pipeline_layout = shader_res_mgr_.get_pipeline_layout(); - - // 分配描述符集 - auto desc_set = shader_res_mgr_.allocate_descriptor_set(desc_layout); - if (!desc_set) { - return; - } - - // 更新 Uniform Buffer 描述符 - if (command.params_data && command.params_size > 0) { - auto allocation = shader_res_mgr_.allocate_uniform_buffer(command.params_size); - if (allocation.mapped_ptr) { - std::memcpy(allocation.mapped_ptr, command.params_data, command.params_size); - - for (const auto& binding : command.bindings) { - if (binding.type == vk::DescriptorType::eUniformBuffer) { - shader_res_mgr_.update_uniform_buffer_descriptor( - desc_set, - binding.binding, - allocation.buffer, - allocation.offset, - allocation.size - ); - } - } - } - } - - // 绑定 Pipeline - cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - - // 绑定描述符集 - cmd.bindDescriptorSets( - vk::PipelineBindPoint::eGraphics, - pipeline_layout, - 0, 1, &desc_set, - 0, nullptr - ); - - // 设置视口和裁剪 - vk::Viewport viewport{0.0f, 0.0f, viewport_size_.x(), viewport_size_.y(), 0.0f, 1.0f}; - cmd.setViewport(0, 1, &viewport); - - vk::Rect2D scissor{{0, 0}, {static_cast(viewport_size_.x()), static_cast(viewport_size_.y())}}; - cmd.setScissor(0, 1, &scissor); - - // 上传顶点数据到 GPU - vk::Buffer vertex_buffer = VK_NULL_HANDLE; - VkDeviceSize vertex_offset = 0; - - if (command.vertex_config.has_value()) { - const auto& vc = command.vertex_config.value(); - - // 分配顶点 Buffer - vertex_buffer = shader_res_mgr_.allocate_vertex_buffer(vc.data_size); - if (vertex_buffer) { - // 映射并复制数据 - VkMemoryRequirements mem_requirements; - vkGetBufferMemoryRequirements(device_.get_handle(), vertex_buffer, &mem_requirements); - - VkMemoryAllocateInfo alloc_info{}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = mem_requirements.size; - alloc_info.memoryTypeIndex = res_mgr_.find_memory_type( - mem_requirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE | VK_MEMORY_PROPERTY_HOST_COHERENT - ); - - VkDeviceMemory memory; - vkAllocateMemory(device_.get_handle(), &alloc_info, nullptr, &memory); - vkBindBufferMemory(device_.get_handle(), vertex_buffer, memory, 0); - - void* data_ptr; - vkMapMemory(device_.get_handle(), memory, 0, vc.data_size, 0, &data_ptr); - std::memcpy(data_ptr, vc.data, vc.data_size); - vkUnmapMemory(device_.get_handle(), memory); - - vertex_offset = 0; - } - } - - // 绑定顶点缓冲 - if (vertex_buffer) { - cmd.bindVertexBuffers(0, 1, &vertex_buffer, &vertex_offset); - } - - // 处理索引缓冲 - if (command.index_config.has_value()) { - const auto& ic = command.index_config.value(); - - // 分配索引 Buffer - auto index_buffer = shader_res_mgr_.allocate_index_buffer(ic.index_count * sizeof(uint32_t)); - if (index_buffer) { - // 映射并复制数据 - VkMemoryRequirements mem_requirements; - vkGetBufferMemoryRequirements(device_.get_handle(), index_buffer, &mem_requirements); - - VkMemoryAllocateInfo alloc_info{}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = mem_requirements.size; - alloc_info.memoryTypeIndex = res_mgr_.find_memory_type( - mem_requirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE | VK_MEMORY_PROPERTY_HOST_COHERENT - ); - - VkDeviceMemory memory; - vkAllocateMemory(device_.get_handle(), &alloc_info, nullptr, &memory); - vkBindBufferMemory(device_.get_handle(), index_buffer, memory, 0); - - void* data_ptr; - vkMapMemory(device_.get_handle(), memory, 0, ic.index_count * sizeof(uint32_t), 0, &data_ptr); - std::memcpy(data_ptr, ic.data, ic.index_count * sizeof(uint32_t)); - vkUnmapMemory(device_.get_handle(), memory); - - // 绑定索引缓冲 - cmd.bindIndexBuffer(index_buffer, 0, vk::IndexType::eUint32); - - // 绘制索引几何 - cmd.drawIndexed( - static_cast(ic.index_count), - 1, - 0, - 0, - 0 - ); - } - } - else { - // 无索引缓冲,直接绘制 - uint32_t vertex_count = command.vertex_config.has_value() - ? static_cast(command.vertex_config.value().vertex_count) - : 0; - - if (vertex_count > 0) { - cmd.draw(vertex_count, 1, 0, 0); - } - } -} - -// ============================================================================ -// 辅助方法 -// ============================================================================ - -auto custom_shader_widget_renderer::setup_pipeline_and_descriptors( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - vk::DescriptorPool descriptor_pool, - vk::ImageView source_texture_view -) -> std::tuple { - // TODO: 实现完整的 Pipeline 和描述符设置 - return {VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE}; -} - -void custom_shader_widget_renderer::push_constants( - vk::CommandBuffer cmd, - vk::PipelineLayout layout, - const custom_shader_widget_command& command -) { - // 使用 create_push_constants 创建标准 push constants - static auto start_time = std::chrono::high_resolution_clock::now(); - auto current_time = std::chrono::high_resolution_clock::now(); - float time = std::chrono::duration(current_time - start_time).count(); - - auto constants = create_push_constants(viewport_size_.x(), viewport_size_.y(), time); - - cmd.pushConstants( - layout, - vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, - 0, - sizeof(push_constants), - &constants - ); -} - -void custom_shader_widget_renderer::set_stencil_ref(uint8_t ref) { - stencil_ref_ = ref; -} - -auto custom_shader_widget_renderer::get_stencil_ref() const -> uint8_t { - return stencil_ref_; -} - -void custom_shader_widget_renderer::cleanup() { - // RAII 句柄自动管理资源销毁 - procedural_quad_buffer_.reset(); - procedural_quad_index_buffer_.reset(); -} - -} // namespace mirage \ No newline at end of file diff --git a/src/render/renderers/custom_shader_widget_renderer.h b/src/render/renderers/custom_shader_widget_renderer.h index 8eee4bf..a2f0ee9 100644 --- a/src/render/renderers/custom_shader_widget_renderer.h +++ b/src/render/renderers/custom_shader_widget_renderer.h @@ -12,167 +12,165 @@ #include "ui/widgets/custom_shader/custom_shader_widget_types.h" #include +#include "render/vulkan/typed_buffer.h" + namespace mirage { + // 前向声明 + struct custom_shader_buffers; -// 前向声明 -struct custom_shader_buffers; + /// @brief 程序化四边形 Push Constants + /// @details 用于 procedural 模式,通过 push constant 传递矩形信息 + struct procedural_quad_push_constants { + vec4f_t rect; ///< x, y, width, height (16 bytes) + vec4f_t color; ///< RGBA (16 bytes) + }; -/// @brief 程序化四边形 Push Constants -/// @details 用于 procedural 模式,通过 push constant 传递矩形信息 -struct procedural_quad_push_constants { - glm::vec4 rect; ///< x, y, width, height - glm::vec4 color; ///< RGBA - float corner_radius; ///< 圆角半径 - float padding[3]; ///< 对齐填充(16字节对齐) -}; + static_assert(sizeof(procedural_quad_push_constants) == 32, + "procedural_quad_push_constants must be 32 bytes"); + static_assert(sizeof(procedural_quad_push_constants) % 16 == 0, + "procedural_quad_push_constants must be 16-byte aligned"); -static_assert(sizeof(procedural_quad_push_constants) == 32, - "procedural_quad_push_constants must be 32 bytes"); -static_assert(sizeof(procedural_quad_push_constants) % 16 == 0, - "procedural_quad_push_constants must be 16-byte aligned"); + /// @brief 自定义着色器控件渲染器 + /// + /// 负责渲染 custom_shader_widget_command,支持三种渲染模式: + /// - **程序化渲染**:使用无顶点缓冲区的四边形 + /// - **后处理模式**:先渲染子控件到临时目标,再应用着色器 + /// - **自定义几何**:使用用户提供的顶点数据 + /// + /// 与 shader_resource_manager 配合进行 Pipeline 缓存和资源管理。 + class custom_shader_widget_renderer { + public: + /// @brief 构造函数 + /// @param device 逻辑设备 + /// @param res_mgr 资源管理器 + /// @param shader_res_mgr 着色器资源管理器 + custom_shader_widget_renderer( + logical_device& device, + resource_manager& res_mgr, + shader_resource_manager& shader_res_mgr + ); -/// @brief 自定义着色器控件渲染器 -/// -/// 负责渲染 custom_shader_widget_command,支持三种渲染模式: -/// - **程序化渲染**:使用无顶点缓冲区的四边形 -/// - **后处理模式**:先渲染子控件到临时目标,再应用着色器 -/// - **自定义几何**:使用用户提供的顶点数据 -/// -/// 与 shader_resource_manager 配合进行 Pipeline 缓存和资源管理。 -class custom_shader_widget_renderer { -public: - /// @brief 构造函数 - /// @param device 逻辑设备 - /// @param res_mgr 资源管理器 - /// @param shader_res_mgr 着色器资源管理器 - custom_shader_widget_renderer( - logical_device& device, - resource_manager& res_mgr, - shader_resource_manager& shader_res_mgr - ); + ~custom_shader_widget_renderer(); - ~custom_shader_widget_renderer(); + // 禁止拷贝 + custom_shader_widget_renderer(const custom_shader_widget_renderer&) = delete; + custom_shader_widget_renderer& operator=(const custom_shader_widget_renderer&) = delete; - // 禁止拷贝 - custom_shader_widget_renderer(const custom_shader_widget_renderer&) = delete; - custom_shader_widget_renderer& operator=(const custom_shader_widget_renderer&) = delete; + /// @brief 初始化渲染器 + /// @param render_pass 渲染通道 + void initialize(vk::RenderPass render_pass); - /// @brief 初始化渲染器 - /// @param render_pass 渲染通道 - void initialize(vk::RenderPass render_pass); + /// @brief 设置视口大小 + /// @param viewport_size 视口大小 + void set_viewport(const vec2f_t& viewport_size); - /// @brief 设置视口大小 - /// @param viewport_size 视口大小 - void set_viewport(const vec2f_t& viewport_size); + /// @brief 设置目标池(用于 post_process 模式的临时目标) + void set_target_pool(unified_target_pool* pool); - /// @brief 设置目标池(用于 post_process 模式的临时目标) - void set_target_pool(unified_target_pool* pool); + // ======================================================================== + // 类型安全渲染接口 + // ======================================================================== - // ======================================================================== - // 类型安全渲染接口 - // ======================================================================== + /// @brief 渲染自定义着色器控件(离屏 Pass) + /// @tparam State 当前 Pass 状态 + /// @param ctx 帧上下文 + /// @param command 渲染命令 + /// @param buffers 外部顶点缓冲区(可选,用于标准 ui_vertex) + /// @param descriptor_pool 描述符池 + template + void render( + frame_context& ctx, + const custom_shader_widget_command& command, + custom_shader_buffers* buffers, + vk::DescriptorPool descriptor_pool + ) { + render_impl(ctx.cmd(), command, buffers, descriptor_pool); + } - /// @brief 渲染自定义着色器控件(离屏 Pass) - /// @tparam State 当前 Pass 状态 - /// @param ctx 帧上下文 - /// @param command 渲染命令 - /// @param buffers 外部顶点缓冲区(可选,用于标准 ui_vertex) - /// @param descriptor_pool 描述符池 - template - void render( - frame_context& ctx, - const custom_shader_widget_command& command, - custom_shader_buffers* buffers, - vk::DescriptorPool descriptor_pool - ) { - render_impl(ctx.cmd(), command, buffers, descriptor_pool); - } + // ======================================================================== + // 低级接口 + // ======================================================================== - // ======================================================================== - // 低级接口 - // ======================================================================== + /// @brief 渲染实现 + void render_impl( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + custom_shader_buffers* buffers, + vk::DescriptorPool descriptor_pool + ); - /// @brief 渲染实现 - void render_impl( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - custom_shader_buffers* buffers, - vk::DescriptorPool descriptor_pool - ); + /// @brief 设置 Stencil 参考值 + void set_stencil_ref(uint8_t ref); - /// @brief 设置 Stencil 参考值 - void set_stencil_ref(uint8_t ref); + /// @brief 获取 Stencil 参考值 + [[nodiscard]] auto get_stencil_ref() const -> uint8_t; - /// @brief 获取 Stencil 参考值 - [[nodiscard]] auto get_stencil_ref() const -> uint8_t; + /// @brief 清理资源 + void cleanup(); - /// @brief 清理资源 - void cleanup(); + private: + logical_device& device_; + resource_manager& res_mgr_; + shader_resource_manager& shader_res_mgr_; + unified_target_pool* target_pool_ = nullptr; -private: - logical_device& device_; - resource_manager& res_mgr_; - shader_resource_manager& shader_res_mgr_; - unified_target_pool* target_pool_ = nullptr; + vk::RenderPass render_pass_; + vec2f_t viewport_size_{0, 0}; + uint8_t stencil_ref_ = 0; - vk::RenderPass render_pass_; - vec2f_t viewport_size_{0, 0}; - uint8_t stencil_ref_ = 0; + // 默认顶点着色器 SPIR-V(从编译的头文件引入) + std::span default_vert_spirv_; + std::span procedural_vert_spirv_; - // 默认顶点着色器 SPIR-V(从编译的头文件引入) - std::span default_vert_spirv_; - std::span procedural_vert_spirv_; + // 程序化四边形顶点缓冲(用于 custom_geometry 模式) + device_handle_t procedural_quad_buffer_; + device_handle_t procedural_quad_index_buffer_; + VkDeviceSize procedural_quad_vertex_offset_ = 0; + VkDeviceSize procedural_quad_index_offset_ = 0; - // 程序化四边形顶点缓冲(用于 custom_geometry 模式) - device_handle_t procedural_quad_buffer_; - device_handle_t procedural_quad_index_buffer_; - VkDeviceSize procedural_quad_vertex_offset_ = 0; - VkDeviceSize procedural_quad_index_offset_ = 0; + // 内部方法 + void create_procedural_quad_buffer(); - // 内部方法 - void create_procedural_quad_buffer(); + void render_procedural( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + vk::DescriptorPool descriptor_pool + ); - void render_procedural( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - vk::DescriptorPool descriptor_pool - ); + void render_post_process( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + custom_shader_buffers* buffers, + vk::DescriptorPool descriptor_pool + ); - void render_post_process( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - custom_shader_buffers* buffers, - vk::DescriptorPool descriptor_pool - ); + void render_custom_geometry( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + vk::DescriptorPool descriptor_pool + ); - void render_custom_geometry( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - vk::DescriptorPool descriptor_pool - ); + auto setup_pipeline_and_descriptors( + vk::CommandBuffer cmd, + const custom_shader_widget_command& command, + vk::DescriptorPool descriptor_pool, + vk::ImageView source_texture_view = VK_NULL_HANDLE + ) -> std::tuple; - auto setup_pipeline_and_descriptors( - vk::CommandBuffer cmd, - const custom_shader_widget_command& command, - vk::DescriptorPool descriptor_pool, - vk::ImageView source_texture_view = VK_NULL_HANDLE - ) -> std::tuple; + void push_constants( + vk::CommandBuffer cmd, + vk::PipelineLayout layout, + const custom_shader_widget_command& command + ); + }; - void push_constants( - vk::CommandBuffer cmd, - vk::PipelineLayout layout, - const custom_shader_widget_command& command - ); -}; + /// @brief 自定义着色器控件的顶点缓冲区 + /// @details 由 per_window_vertex_buffers 管理 + struct custom_shader_buffers { + typed_buffer_owned vertices; + typed_buffer_owned indices; -/// @brief 自定义着色器控件的顶点缓冲区 -/// @details 由 per_window_vertex_buffers 管理 -struct custom_shader_buffers { - typed_buffer_owned vertices; - typed_buffer_owned indices; - - size_t vertex_count = 0; - size_t index_count = 0; -}; - -} // namespace mirage \ No newline at end of file + size_t vertex_count = 0; + size_t index_count = 0; + }; +} // namespace mirage diff --git a/src/render/uniform_types.h b/src/render/uniform_types.h index f765a0e..befa3fd 100644 --- a/src/render/uniform_types.h +++ b/src/render/uniform_types.h @@ -2,38 +2,84 @@ #include #include +#include + +#include "render/push_constant_traits.h" namespace mirage { /** - * @brief Push Constants 结构 + * @brief Push Constants 基础部分(由渲染器自动管理) * - * 用于传递全局渲染参数到着色器。 - * 使用 Push Constants 替代 Uniform Buffer 以提高频繁更新数据的性能。 + * 所有后处理着色器共享的基础参数,包括投影矩阵、视口大小和时间。 + * 此部分由渲染器自动填充,大小固定为 80 字节。 * * 对应 GLSL 布局: * @code - * layout(push_constant) uniform Constants { - * mat4 projection; - * vec2 viewport_size; - * vec2 screen_resolution; - * float time; - * // padding 由编译器自动处理 - * } constants; + * layout(push_constant) uniform PushConstants { + * mat4 projection; // 偏移 0, 大小 64 + * vec2 viewport_size; // 偏移 64, 大小 8 + * float time; // 偏移 72, 大小 4 + * float _pad0; // 偏移 76, 大小 4 + * } pc; * @endcode */ - struct push_constants { + struct push_constants_base { float projection[16]; ///< 正交投影矩阵(列主序,符合 GLSL mat4 布局) float viewport_size[2]; ///< 视口大小 (width, height) - float screen_resolution[2]; ///< 屏幕分辨率 (width, height),用于像素对齐 float time; ///< 时间(秒),用于动画效果 - float padding[3]; ///< 对齐填充,确保结构体大小为 16 字节的倍数 + float _pad0; ///< 对齐填充 }; - // 静态断言确保结构体大小符合 std430/std140 对齐要求 - static_assert(sizeof(push_constants) == (16 + 2 + 2 + 1 + 3) * sizeof(float), - "push_constants size must be 96 bytes"); - static_assert(sizeof(push_constants) % 16 == 0, - "push_constants must be 16-byte aligned"); + static_assert(sizeof(push_constants_base) == 80, "push_constants_base must be exactly 80 bytes"); + + /// @brief Push Constants 基础部分结束偏移量(效果部分开始偏移) + inline constexpr size_t PUSH_CONSTANT_BASE_OFFSET = 80; + + /** + * @brief Push Constants 标准基础部分期望布局 + * + * 所有使用标准基础部分的着色器必须匹配此布局。 + * 此结构体用于编译期验证着色器声明的 push_constant 布局与 C++ 端定义的一致性。 + * + * 期望布局: + * - projection: 偏移 0, 大小 64 + * - viewport_size: 偏移 64, 大小 8 + * - time: 偏移 72, 大小 4 + * - _pad0: 偏移 76, 大小 4 + * - total_size: 80 + */ + struct expected_push_constant_base_layout { + static constexpr bool has_projection = true; + static constexpr bool has_viewport_size = true; + static constexpr bool has_time = true; + static constexpr bool has_pad0 = true; + + static constexpr size_t projection_offset = 0; + static constexpr size_t viewport_size_offset = 64; + static constexpr size_t time_offset = 72; + static constexpr size_t pad0_offset = 76; + + static constexpr size_t projection_size = 64; + static constexpr size_t viewport_size_size = 8; + static constexpr size_t time_size = 4; + static constexpr size_t pad0_size = 4; + + static constexpr size_t total_size = 80; + }; + + // 验证 push_constants_base 与期望布局一致 + static_assert(offsetof(push_constants_base, projection) == + expected_push_constant_base_layout::projection_offset, + "push_constants_base::projection offset mismatch"); + static_assert(offsetof(push_constants_base, viewport_size) == + expected_push_constant_base_layout::viewport_size_offset, + "push_constants_base::viewport_size offset mismatch"); + static_assert(offsetof(push_constants_base, time) == + expected_push_constant_base_layout::time_offset, + "push_constants_base::time offset mismatch"); + static_assert(offsetof(push_constants_base, _pad0) == + expected_push_constant_base_layout::pad0_offset, + "push_constants_base::_pad0 offset mismatch"); /** * @brief 创建正交投影矩阵 @@ -59,6 +105,61 @@ namespace mirage { }; } + /** + * @brief 创建 Push Constants 基础部分 + * + * @param width 视口宽度 + * @param height 视口高度 + * @param time 当前时间(秒) + * @return 初始化的 push_constants_base 结构 + */ + inline auto create_push_constants_base(float width, float height, float time = 0.0f) + -> push_constants_base { + const auto proj = create_ortho_projection(width, height); + + push_constants_base base{}; + std::memcpy(base.projection, proj.data(), sizeof(proj)); + base.viewport_size[0] = width; + base.viewport_size[1] = height; + base.time = time; + return base; + } + + /** + * @brief Push Constants 结构(旧版,保留兼容性) + * + * 用于传递全局渲染参数到着色器。 + * 使用 Push Constants 替代 Uniform Buffer 以提高频繁更新数据的性能。 + * + * 对应 GLSL 布局: + * @code + * layout(push_constant) uniform Constants { + * mat4 projection; + * vec2 viewport_size; + * vec2 screen_resolution; + * float time; + * // padding 由编译器自动处理 + * } constants; + * @endcode + */ + struct push_constants { + float projection[16]; ///< 正交投影矩阵(列主序,符合 GLSL mat4 布局) + float viewport_size[2]; ///< 视口大小 (width, height) + float screen_resolution[2]; ///< 屏幕分辨率 (width, height),用于像素对齐 + float time; ///< 时间(秒),用于动画效果 + float padding[3]; ///< 对齐填充,确保结构体大小为 16 字节的倍数 + }; + + // 静态断言确保结构体大小符合 std430/std140 对齐要求 + + static_assert(sizeof(push_constants) == (16 + 2 + 2 + 1 + 3) * sizeof(float), + "push_constants size must be 96 bytes"); + + + static_assert(sizeof(push_constants) % 16 == 0, + "push_constants must be 16-byte aligned"); + + /** * @brief 创建默认的全局 uniform 数据 * @@ -73,14 +174,39 @@ namespace mirage { for (size_t i = 0; i < 16; ++i) { constants.projection[i] = proj[i]; } - constants.viewport_size[0] = width; - constants.viewport_size[1] = height; - constants.screen_resolution[0] = width; // 默认屏幕分辨率等于视口大小 + constants.viewport_size[0] = width; + constants.viewport_size[1] = height; + constants.screen_resolution[0] = width; // 默认屏幕分辨率等于视口大小 constants.screen_resolution[1] = height; - constants.time = time; - constants.padding[0] = 0.0f; - constants.padding[1] = 0.0f; - constants.padding[2] = 0.0f; + constants.time = time; + constants.padding[0] = 0.0f; + constants.padding[1] = 0.0f; + constants.padding[2] = 0.0f; return constants; } + + /** + * @brief 验证生成的布局与实际结构的一致性 + * + * @tparam Layout 布局类型(由代码生成器生成) + * @return 如果布局是标准的,返回 true;否则返回 false + * + * 使用方法: + * @code + * // 在生成的头文件中使用: + * static_assert(validate_push_constants_base_layout(), + * "Blur push constant base layout mismatch"); + * @endcode + */ + template + constexpr bool validate_push_constants_base_layout() { + if constexpr (standard_push_constant_base_layout) { + static_assert(Layout::projection_offset == offsetof(push_constants_base, projection)); + static_assert(Layout::viewport_size_offset == offsetof(push_constants_base, viewport_size)); + static_assert(Layout::time_offset == offsetof(push_constants_base, time)); + static_assert(Layout::total_size == sizeof(push_constants_base)); + return true; + } + return false; + } } // namespace mirage diff --git a/src/render/vulkan/pipeline/compute_pipeline.cpp b/src/render/vulkan/pipeline/compute_pipeline.cpp index bc5176c..4ce483a 100644 --- a/src/render/vulkan/pipeline/compute_pipeline.cpp +++ b/src/render/vulkan/pipeline/compute_pipeline.cpp @@ -8,7 +8,7 @@ namespace mirage { auto create_pipeline_internal( const logical_device& device, vk::ShaderModule shader_module, - std::span bindings + std::span bindings ) -> expected_t { vk::Device vk_device = device.get_handle(); @@ -123,7 +123,7 @@ namespace mirage { } auto compute_pipeline::create(const logical_device& device, const std::string& shader_path, - std::span bindings) -> expected_t { + std::span bindings) -> expected_t { auto module_ret = graphics_pipeline::load_shader_module(device, shader_path); if (!module_ret) return std::unexpected(module_ret.error()); @@ -138,7 +138,7 @@ namespace mirage { } auto compute_pipeline::create_from_spv(const logical_device& device, std::span spv_code, - std::span bindings) -> expected_t< + std::span bindings) -> expected_t< compute_pipeline, vk::Result> { auto module_ret = graphics_pipeline::load_shader_module_from_memory(device, spv_code); if (!module_ret) diff --git a/src/render/vulkan/pipeline/compute_pipeline.h b/src/render/vulkan/pipeline/compute_pipeline.h index 8e873db..749f827 100644 --- a/src/render/vulkan/pipeline/compute_pipeline.h +++ b/src/render/vulkan/pipeline/compute_pipeline.h @@ -12,9 +12,9 @@ namespace mirage { public: // Constructors / Factory static auto create(const logical_device& device, const std::string& shader_path, - std::span bindings = {}) -> expected_t; + std::span bindings = {}) -> expected_t; static auto create_from_spv(const logical_device& device, std::span spv_code, - std::span bindings = {}) -> expected_t< + std::span bindings = {}) -> expected_t< compute_pipeline, vk::Result>; // Interface diff --git a/src/render/vulkan/resource_manager.h b/src/render/vulkan/resource_manager.h index d4a5541..d027245 100644 --- a/src/render/vulkan/resource_manager.h +++ b/src/render/vulkan/resource_manager.h @@ -13,6 +13,7 @@ #include "render/vulkan/descriptor_manager.h" #include #include +#include namespace mirage { /** @@ -268,6 +269,34 @@ namespace mirage { */ [[nodiscard]] auto get_device() const noexcept -> vk::Device { return device_; } + /** + * @brief 获取 Vulkan 物理设备 + * @return Vulkan 物理设备句柄 + */ + [[nodiscard]] auto get_physical_device() const noexcept -> vk::PhysicalDevice { + return physical_device_; + } + + /** + * @brief 查找符合要求的内存类型 + * + * @param type_bits 内存类型位掩码 + * @param properties 内存属性标志 + * @return 内存类型索引 + */ + [[nodiscard]] auto find_memory_type(uint32_t type_bits, vk::MemoryPropertyFlags properties) const + -> uint32_t { + vk::PhysicalDeviceMemoryProperties mem_properties; + physical_device_.getMemoryProperties(&mem_properties); + + for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) { + if ((type_bits & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties)) { + return i; + } + } + + throw std::runtime_error("Failed to find suitable memory type"); + } private: // ======================================================================== // 核心成员 diff --git a/src/render/vulkan/shader_resource_manager.cpp b/src/render/vulkan/shader_resource_manager.cpp index 2d8bbc5..4e54cdd 100644 --- a/src/render/vulkan/shader_resource_manager.cpp +++ b/src/render/vulkan/shader_resource_manager.cpp @@ -35,7 +35,7 @@ void shader_resource_manager::initialize(vk::RenderPass render_pass, uint32_t fr // 获取 Uniform Buffer 对齐要求 VkPhysicalDeviceProperties properties; vkGetPhysicalDeviceProperties( - res_mgr_.get_device().get_handle().get(), + res_mgr_.get_physical_device(), &properties ); min_uniform_buffer_offset_alignment_ = properties.limits.minUniformBufferOffsetAlignment; @@ -101,9 +101,16 @@ void shader_resource_manager::create_per_frame_uniform_buffer(uint32_t frame_ind } void shader_resource_manager::create_pipeline_layout() { - // 创建空的 Pipeline Layout(不使用 push constants 或 desc sets) - // Descriptor Set Layout 由各 Pipeline 单独管理 + // 创建共享的 Pipeline Layout,支持完整的 Push Constants(128 字节) + // 基础部分 80 字节 + 效果部分 48 字节 + vk::PushConstantRange push_constant_range{ + vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, + 0, // 偏移 + 128 // 大小(基础80 + 效果48,最大保证大小) + }; + vk::PipelineLayoutCreateInfo create_info{}; + create_info.setPushConstantRanges(push_constant_range); auto [result, layout] = device_.createPipelineLayout(create_info); if (result == vk::Result::eSuccess) { @@ -155,8 +162,8 @@ auto shader_resource_manager::get_or_create_pipeline( std::span vert_spirv, std::span frag_spirv, std::span bindings, - std::span vertex_bindings, - std::span vertex_attributes, + std::span vertex_bindings, + std::span vertex_attributes, VkRenderPass render_pass ) -> vk::Pipeline { std::lock_guard lock(pipeline_mutex_); @@ -279,27 +286,23 @@ auto shader_resource_manager::create_pipeline( device_handle_t vert_module, device_handle_t frag_module, vk::DescriptorSetLayout desc_layout, - std::span vertex_bindings, - std::span vertex_attributes, + std::span vertex_bindings, + std::span vertex_attributes, VkRenderPass render_pass ) -> pipeline_resources_t { pipeline_resources_t resources; // Shader Stage - std::array shader_stages = { - vk::PipelineShaderStageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = vk::ShaderStageFlagBits::eVertex, - .module = vert_module.get(), - .pName = "main" - }, - vk::PipelineShaderStageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = vk::ShaderStageFlagBits::eFragment, - .module = frag_module.get(), - .pName = "main" - } - }; + vk::PipelineShaderStageCreateInfo vertex_stage_info{}; + vertex_stage_info.setModule(vert_module); + vertex_stage_info.setStage(vk::ShaderStageFlagBits::eVertex); + vertex_stage_info.setPName("main"); + vk::PipelineShaderStageCreateInfo fragment_stage_info{}; + fragment_stage_info.setModule(frag_module); + fragment_stage_info.setStage(vk::ShaderStageFlagBits::eFragment); + fragment_stage_info.setPName("main"); + + std::array shader_stages = {vertex_stage_info, fragment_stage_info}; // Vertex Input vk::PipelineVertexInputStateCreateInfo vertex_input_info{}; @@ -318,7 +321,8 @@ auto shader_resource_manager::create_pipeline( viewport.maxDepth = 1.0f; vk::Rect2D scissor{}; - scissor.extent = {1, 1}; + scissor.extent.setHeight(1); + scissor.extent.setWidth(1); vk::PipelineViewportStateCreateInfo viewport_info{}; viewport_info.setViewports(viewport); @@ -354,9 +358,16 @@ auto shader_resource_manager::create_pipeline( vk::PipelineColorBlendStateCreateInfo color_blend_info{}; color_blend_info.setAttachments(color_blend_attachment); - // Pipeline Layout + // Pipeline Layout(支持完整的 Push Constants) + vk::PushConstantRange push_constant_range{ + vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, + 0, // 偏移 + 128 // 大小(基础80 + 效果48,最大保证大小) + }; + vk::PipelineLayoutCreateInfo pipeline_layout_info{}; pipeline_layout_info.setSetLayouts(desc_layout); + pipeline_layout_info.setPushConstantRanges(push_constant_range); auto [layout_result, pipeline_layout] = device_.createPipelineLayout(pipeline_layout_info); if (layout_result != vk::Result::eSuccess) { @@ -377,7 +388,7 @@ auto shader_resource_manager::create_pipeline( pipeline_info.renderPass = render_pass; pipeline_info.subpass = 0; - auto [pipeline_result, pipeline] = device_.createGraphicsPipelines( + auto [pipeline_result, pipeline] = device_.createGraphicsPipeline( VK_NULL_HANDLE, pipeline_info ); @@ -405,8 +416,8 @@ auto shader_resource_manager::create_pipeline( } auto shader_resource_manager::compute_vertex_layout_hash( - std::span bindings, - std::span attributes + std::span bindings, + std::span attributes ) -> std::size_t { std::size_t h = 0; @@ -450,13 +461,12 @@ auto shader_resource_manager::get_or_create_descriptor_set_layout( vk_bindings.reserve(bindings.size()); for (const auto& b : bindings) { - vk_bindings.push_back(vk::DescriptorSetLayoutBinding{ - .binding = b.binding, - .descriptorType = b.type, - .descriptorCount = b.count, - .stageFlags = b.stage, - .pImmutableSamplers = nullptr - }); + vk::DescriptorSetLayoutBinding layout_binding{}; + layout_binding.setBinding(b.binding); + layout_binding.setDescriptorType(b.type); + layout_binding.setDescriptorCount(b.count); + layout_binding.setStageFlags(b.stage); + vk_bindings.push_back(layout_binding); } // 创建 Layout @@ -513,14 +523,12 @@ void shader_resource_manager::update_uniform_buffer_descriptor( size }; - vk::WriteDescriptorSet write{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = set, - .dstBinding = binding, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_info - }; + vk::WriteDescriptorSet write{}; + write.setDstSet(set); + write.setDstBinding(binding); + write.setDescriptorCount(1); + write.setDescriptorType(vk::DescriptorType::eUniformBuffer); + write.setPBufferInfo(&buffer_info); device_.updateDescriptorSets(write, nullptr); } @@ -537,14 +545,12 @@ void shader_resource_manager::update_sampler_descriptor( vk::ImageLayout::eShaderReadOnlyOptimal }; - vk::WriteDescriptorSet write{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = set, - .dstBinding = binding, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &image_info - }; + vk::WriteDescriptorSet write{}; + write.setDstSet(set); + write.setDstBinding(binding); + write.setDescriptorCount(1); + write.setDescriptorType(vk::DescriptorType::eCombinedImageSampler); + write.setPImageInfo(&image_info); device_.updateDescriptorSets(write, nullptr); } diff --git a/src/render/vulkan/shader_resource_manager.h b/src/render/vulkan/shader_resource_manager.h index 81e729e..2a73153 100644 --- a/src/render/vulkan/shader_resource_manager.h +++ b/src/render/vulkan/shader_resource_manager.h @@ -110,7 +110,7 @@ struct layout_cache_key { [[nodiscard]] auto operator==(const layout_cache_key& other) const noexcept -> bool { return bindings.size() == other.bindings.size() && - std::equal(bindings.begin(), bindings.end(), other.bindings.begin()); + std::ranges::equal(bindings, other.bindings); } }; @@ -124,7 +124,7 @@ struct layout_cache_key_hash { h ^= std::hash{}(b.binding + 0x9e3779b9 + (h << 6) + (h >> 2)); h ^= std::hash{}(static_cast(b.type) + 0x9e3779b9 + (h << 6) + (h >> 2)); h ^= std::hash{}(b.count + 0x9e3779b9 + (h << 6) + (h >> 2)); - h ^= std::hash{}(static_cast(b.stage) + 0x9e3779b9 + (h << 6) + (h >> 2)); + h ^= std::hash{}(static_cast(b.stage) + 0x9e3779b9 + (h << 6) + (h >> 2)); } return h; } @@ -216,8 +216,8 @@ public: std::span vert_spirv, std::span frag_spirv, std::span bindings, - std::span vertex_bindings, - std::span vertex_attributes, + std::span vertex_bindings, + std::span vertex_attributes, VkRenderPass render_pass ) -> vk::Pipeline; @@ -382,6 +382,20 @@ public: */ void cleanup(); + // ======================================================================== + // 公共辅助方法 + // ======================================================================== + + /** + * @brief 计算顶点布局哈希 + * @param bindings 顶点绑定描述 + * @param attributes 顶点属性描述 + * @return 哈希值 + */ + [[nodiscard]] static auto compute_vertex_layout_hash( + std::span bindings, + std::span attributes + ) -> std::size_t; private: // ======================================================================== // 私有方法 @@ -410,8 +424,8 @@ private: device_handle_t vert_module, device_handle_t frag_module, vk::DescriptorSetLayout desc_layout, - std::span vertex_bindings, - std::span vertex_attributes, + std::span vertex_bindings, + std::span vertex_attributes, VkRenderPass render_pass ) -> pipeline_resources_t; @@ -438,17 +452,6 @@ private: */ void create_per_frame_uniform_buffer(uint32_t frame_index, size_t capacity); - /** - * @brief 计算顶点布局哈希 - * @param bindings 顶点绑定描述 - * @param attributes 顶点属性描述 - * @return 哈希值 - */ - [[nodiscard]] static auto compute_vertex_layout_hash( - std::span bindings, - std::span attributes - ) -> std::size_t; - // ======================================================================== // 成员变量 // ======================================================================== diff --git a/src/render/vulkan/vulkan_common.h b/src/render/vulkan/vulkan_common.h index 0ea0353..64d7bbc 100644 --- a/src/render/vulkan/vulkan_common.h +++ b/src/render/vulkan/vulkan_common.h @@ -36,5 +36,12 @@ namespace mirage { vk::DescriptorType type; ///< 描述符类型 uint32_t count; ///< 描述符数量 vk::ShaderStageFlags stage; ///< 着色器阶段 + + bool operator==(const shader_binding_info& other) const { + return binding == other.binding && + type == other.type && + count == other.count && + stage == other.stage; + } }; } // namespace mirage diff --git a/src/ui/widgets/custom_shader/custom_shader_widget.h b/src/ui/widgets/custom_shader/custom_shader_widget.h index b543123..9310d25 100644 --- a/src/ui/widgets/custom_shader/custom_shader_widget.h +++ b/src/ui/widgets/custom_shader/custom_shader_widget.h @@ -5,216 +5,216 @@ #include namespace mirage { + /// @brief 自定义着色器控件模板基类 + /// + /// @tparam ParamsT 着色器参数结构体类型(由着色器工具自动生成) + /// @tparam VertexT 顶点类型(默认使用 ui_vertex) + /// + /// 用户通过继承此类创建自定义着色器控件。ParamsT 类型应由着色器工具 + /// 从 GLSL UBO 定义自动生成,确保 C++ 和着色器之间的类型安全。 + /// + /// 支持三种渲染模式: + /// - **程序化渲染**:使用着色器生成内容(无子控件,无源纹理) + /// - **后处理模式**:对子控件渲染结果或指定纹理应用着色器效果 + /// - **自定义几何模式**:使用自定义顶点着色器和顶点缓冲 + /// + /// @example + /// ```cpp + /// // 使用工具生成的类型 + /// #include "gen/shaders/gradient.h" + /// + /// class gradient_widget : public custom_shader_widget { + /// public: + /// gradient_widget() { + /// params().start_color = {1, 0, 0, 1}; + /// params().end_color = {0, 0, 1, 1}; + /// } + /// + /// auto& colors(const vec4f_t& start, const vec4f_t& end) { + /// params().start_color = start; + /// params().end_color = end; + /// mark_params_dirty(); + /// return *this; + /// } + /// + /// protected: + /// auto get_shader_id() const noexcept -> uint32_t override { + /// return hash_compile_time("gradient_widget"); + /// } + /// + /// auto get_fragment_shader_spirv() const -> std::span override { + /// return shaders::gradient_frag_spirv; + /// } + /// + /// auto get_bindings() const -> std::span override { + /// return shaders::gradient_bindings; + /// } + /// + /// auto get_render_mode() const noexcept -> custom_shader_render_mode override { + /// return custom_shader_render_mode::procedural; + /// } + /// }; + /// ``` + template + class custom_shader_widget : public custom_shader_widget_base { + public: + using params_type = ParamsT; + using vertex_type = VertexT; -/// @brief 自定义着色器控件模板基类 -/// -/// @tparam ParamsT 着色器参数结构体类型(由着色器工具自动生成) -/// @tparam VertexT 顶点类型(默认使用 ui_vertex) -/// -/// 用户通过继承此类创建自定义着色器控件。ParamsT 类型应由着色器工具 -/// 从 GLSL UBO 定义自动生成,确保 C++ 和着色器之间的类型安全。 -/// -/// 支持三种渲染模式: -/// - **程序化渲染**:使用着色器生成内容(无子控件,无源纹理) -/// - **后处理模式**:对子控件渲染结果或指定纹理应用着色器效果 -/// - **自定义几何模式**:使用自定义顶点着色器和顶点缓冲 -/// -/// @example -/// ```cpp -/// // 使用工具生成的类型 -/// #include "gen/shaders/gradient.h" -/// -/// class gradient_widget : public custom_shader_widget { -/// public: -/// gradient_widget() { -/// params().start_color = {1, 0, 0, 1}; -/// params().end_color = {0, 0, 1, 1}; -/// } -/// -/// auto& colors(const vec4f_t& start, const vec4f_t& end) { -/// params().start_color = start; -/// params().end_color = end; -/// mark_params_dirty(); -/// return *this; -/// } -/// -/// protected: -/// auto get_shader_id() const noexcept -> uint32_t override { -/// return hash_compile_time("gradient_widget"); -/// } -/// -/// auto get_fragment_shader_spirv() const -> std::span override { -/// return shaders::gradient_frag_spirv; -/// } -/// -/// auto get_bindings() const -> std::span override { -/// return shaders::gradient_bindings; -/// } -/// -/// auto get_render_mode() const noexcept -> custom_shader_render_mode override { -/// return custom_shader_render_mode::procedural; -/// } -/// }; -/// ``` -template -class custom_shader_widget : public custom_shader_widget_base { -public: - using params_type = ParamsT; - using vertex_type = VertexT; + // 静态断言:确保 ParamsT 满足要求 + static_assert(std::is_standard_layout_v, + "ParamsT must be standard layout for GPU compatibility"); + static_assert(alignof(ParamsT) <= 16, + "ParamsT alignment must not exceed 16 bytes"); - // 静态断言:确保 ParamsT 满足要求 - static_assert(std::is_standard_layout_v, - "ParamsT must be standard layout for GPU compatibility"); - static_assert(alignof(ParamsT) <= 16, - "ParamsT alignment must not exceed 16 bytes"); + custom_shader_widget() = default; + ~custom_shader_widget() override = default; - custom_shader_widget() = default; - ~custom_shader_widget() override = default; + // ======================================================================== + // 参数访问 + // ======================================================================== - // ======================================================================== - // 参数访问 - // ======================================================================== + /// @brief 获取参数的可变引用 + /// @note 修改后需要调用 mark_params_dirty() + [[nodiscard]] auto params() noexcept -> ParamsT& { return params_; } - /// @brief 获取参数的可变引用 - /// @note 修改后需要调用 mark_params_dirty() - [[nodiscard]] auto params() noexcept -> ParamsT& { return params_; } + /// @brief 获取参数的只读引用 + [[nodiscard]] auto params() const noexcept -> const ParamsT& { return params_; } - /// @brief 获取参数的只读引用 - [[nodiscard]] auto params() const noexcept -> const ParamsT& { return params_; } + /// @brief 标记参数已修改 + void mark_params_dirty() { + params_dirty_ = true; + mark_render_dirty_internal(); + } - /// @brief 标记参数已修改 - void mark_params_dirty() { - params_dirty_ = true; - mark_render_dirty_internal(); - } + protected: + // ======================================================================== + // 实现基类纯虚函数 + // ======================================================================== -protected: - // ======================================================================== - // 实现基类纯虚函数 - // ======================================================================== + [[nodiscard]] auto get_params_data() const + -> std::pair override { + return {¶ms_, sizeof(ParamsT)}; + } - [[nodiscard]] auto get_params_data() const - -> std::pair override { - return {¶ms_, sizeof(ParamsT)}; - } + [[nodiscard]] auto is_params_dirty() const noexcept -> bool override { + return params_dirty_; + } - [[nodiscard]] auto is_params_dirty() const noexcept -> bool override { - return params_dirty_; - } + void clear_params_dirty() override { + params_dirty_ = false; + } - void clear_params_dirty() override { - params_dirty_ = false; - } + // ======================================================================== + // 自定义顶点支持 + // ======================================================================== - // ======================================================================== - // 自定义顶点支持 - // ======================================================================== + /// @brief 标记顶点数据已修改 + void mark_vertices_dirty() { + vertices_dirty_ = true; + mark_render_dirty_internal(); + } - /// @brief 标记顶点数据已修改 - void mark_vertices_dirty() { - vertices_dirty_ = true; - mark_render_dirty_internal(); - } + [[nodiscard]] auto is_vertices_dirty() const noexcept -> bool override { + return vertices_dirty_; + } - [[nodiscard]] auto is_vertices_dirty() const noexcept -> bool override { - return vertices_dirty_; - } + void clear_vertices_dirty() override { + vertices_dirty_ = false; + } - void clear_vertices_dirty() override { - vertices_dirty_ = false; - } + // ======================================================================== + // 顶点数据管理(仅 custom_geometry 模式使用) + // ======================================================================== - // ======================================================================== - // 顶点数据管理(仅 custom_geometry 模式使用) - // ======================================================================== + /// @brief 获取顶点数据 + [[nodiscard]] auto get_vertices() const noexcept -> const std::vector& { + return vertices_; + } - /// @brief 获取顶点数据 - [[nodiscard]] auto get_vertices() const noexcept -> const std::vector& { - return vertices_; - } + /// @brief 获取顶点数据(可修改) + [[nodiscard]] auto get_vertices() noexcept -> std::vector& { + return vertices_; + } - /// @brief 获取顶点数据(可修改) - [[nodiscard]] auto get_vertices() noexcept -> std::vector& { - return vertices_; - } + /// @brief 设置顶点数据 + void set_vertices(std::vector vertices) { + vertices_ = std::move(vertices); + mark_vertices_dirty(); + } - /// @brief 设置顶点数据 - void set_vertices(std::vector vertices) { - vertices_ = std::move(vertices); - mark_vertices_dirty(); - } + /// @brief 获取索引数据 + [[nodiscard]] auto get_indices() const noexcept -> const std::vector& { + return indices_; + } - /// @brief 获取索引数据 - [[nodiscard]] auto get_indices() const noexcept -> const std::vector& { - return indices_; - } + /// @brief 设置索引数据 + void set_indices(std::vector indices) { + indices_ = std::move(indices); + mark_vertices_dirty(); + } - /// @brief 设置索引数据 - void set_indices(std::vector indices) { - indices_ = std::move(indices); - mark_vertices_dirty(); - } + /// @brief 获取顶点缓冲配置 + [[nodiscard]] auto get_vertex_buffer_config() const + -> std::optional override { + if (vertices_.empty()) { + return std::nullopt; + } - /// @brief 获取顶点缓冲配置 - [[nodiscard]] auto get_vertex_buffer_config() const - -> std::optional override { - if (vertices_.empty()) { - return std::nullopt; - } + return custom_vertex_buffer_config{ + .data = vertices_.data(), + .data_size = vertices_.size() * sizeof(VertexT), + .vertex_count = vertices_.size(), + .binding = get_vertex_binding_description(), + .attributes = get_vertex_attribute_descriptions() + }; + } - return vertex_buffer_config{ - .data = vertices_.data(), - .data_size = vertices_.size() * sizeof(VertexT), - .vertex_count = vertices_.size(), - .binding = get_vertex_binding_description(), - .attributes = get_vertex_attribute_descriptions() - }; - } + /// @brief 获取索引缓冲配置 + [[nodiscard]] auto get_index_buffer_config() const + -> std::optional override { + if (indices_.empty()) { + return std::nullopt; + } - /// @brief 获取索引缓冲配置 - [[nodiscard]] auto get_index_buffer_config() const - -> std::optional override { - if (indices_.empty()) { - return std::nullopt; - } + return index_buffer_config{ + .data = indices_.data(), + .index_count = indices_.size() + }; + } - return index_buffer_config{ - .data = indices_.data(), - .index_count = indices_.size() - }; - } + /// @brief 获取顶点绑定描述(可重写) + [[nodiscard]] virtual auto get_vertex_binding_description() const + -> vk::VertexInputBindingDescription { + if constexpr (requires { VertexT::get_binding_description(); }) { + return VertexT::get_binding_description(); + } + else { + return vk::VertexInputBindingDescription{ + 0, sizeof(VertexT), vk::VertexInputRate::eVertex + }; + } + } - /// @brief 获取顶点绑定描述(可重写) - [[nodiscard]] virtual auto get_vertex_binding_description() const - -> vk::VertexInputBindingDescription { - if constexpr (requires { VertexT::get_binding_description(); }) { - return VertexT::get_binding_description(); - } else { - return vk::VertexInputBindingDescription{ - 0, sizeof(VertexT), vk::VertexInputRate::eVertex - }; - } - } + /// @brief 获取顶点属性描述(可重写) + [[nodiscard]] virtual auto get_vertex_attribute_descriptions() const + -> std::vector { + if constexpr (requires { VertexT::get_attribute_descriptions(); }) { + auto attrs = VertexT::get_attribute_descriptions(); + return std::vector( + attrs.begin(), attrs.end()); + } + else { + return {}; + } + } - /// @brief 获取顶点属性描述(可重写) - [[nodiscard]] virtual auto get_vertex_attribute_descriptions() const - -> std::vector { - if constexpr (requires { VertexT::get_attribute_descriptions(); }) { - auto attrs = VertexT::get_attribute_descriptions(); - return std::vector( - attrs.begin(), attrs.end()); - } else { - return {}; - } - } + private: + ParamsT params_{}; + bool params_dirty_ = true; + bool vertices_dirty_ = false; -private: - ParamsT params_{}; - bool params_dirty_ = true; - bool vertices_dirty_ = false; - - std::vector vertices_; - std::vector indices_; -}; - -} // namespace mirage \ No newline at end of file + std::vector vertices_; + std::vector indices_; + }; +} // namespace mirage diff --git a/src/ui/widgets/custom_shader/custom_shader_widget_base.cpp b/src/ui/widgets/custom_shader/custom_shader_widget_base.cpp index 2d7d1bd..01f1d43 100644 --- a/src/ui/widgets/custom_shader/custom_shader_widget_base.cpp +++ b/src/ui/widgets/custom_shader/custom_shader_widget_base.cpp @@ -3,179 +3,176 @@ #include "render/render_command.h" #include -namespace mirage::ui { +namespace mirage { + struct custom_shader_widget_base::impl { + impl() = default; + }; -struct custom_shader_widget_base::impl { - impl() = default; -}; + custom_shader_widget_base::custom_shader_widget_base() : impl_(std::make_unique()) { + } -custom_shader_widget_base::custom_shader_widget_base() - : impl_(std::make_unique()) { -} + custom_shader_widget_base::~custom_shader_widget_base() = default; -custom_shader_widget_base::~custom_shader_widget_base() = default; + void custom_shader_widget_base::init(const std::shared_ptr& ctx) { + single_child_widget_base::init(ctx); + } -void custom_shader_widget_base::init(const std::shared_ptr& ctx) { - single_child_widget_base::init(ctx); -} + void custom_shader_widget_base::tick(float dt) { + single_child_widget_base::tick(dt); -void custom_shader_widget_base::tick(float dt) { - single_child_widget_base::tick(dt); + // 更新时间 + time_ += dt; - // 更新时间 - time_ += dt; + // 如果需要每帧更新,标记脏状态 + if (is_animated()) { + mark_render_dirty_internal(); + } + } - // 如果需要每帧更新,标记脏状态 - if (is_animated()) { - mark_render_dirty_internal(); - } -} + auto custom_shader_widget_base::do_measure(const vec2f_t& available_size) const -> vec2f_t { + // 如果设置了固定大小,直接返回 + if (fixed_size_.x() > 0 || fixed_size_.y() > 0) { + return fixed_size_; + } -auto custom_shader_widget_base::do_measure(const vec2f_t& available_size) const -> vec2f_t { - // 如果设置了固定大小,直接返回 - if (fixed_size_.x() > 0 || fixed_size_.y() > 0) { - return fixed_size_; - } + // 尝试使用子控件的大小 + auto child = get_child(); + if (child) { + return child->measure(available_size); + } - // 尝试使用子控件的大小 - auto child = get_child(); - if (child) { - return child->measure(available_size); - } + // 如果没有子控件且没有设置固定大小,使用可用空间 + return available_size; + } - // 如果没有子控件且没有设置固定大小,使用可用空间 - return available_size; -} + void custom_shader_widget_base::arrange(const layout_state& state) { + // 使用 layout_write_context 更新状态 + if (auto ctx = get_context()) { + auto write_ctx = ctx->state_store().begin_layout_update( + ctx->current_frame() + ); + write_ctx.update_layout(get_widget_id(), state); + write_ctx.commit(); + } -void custom_shader_widget_base::arrange(const layout_state& state) { - // 使用 layout_write_context 更新状态 - if (auto ctx = get_context()) { - auto write_ctx = ctx->state_store().begin_layout_update( - ctx->current_frame() - ); - write_ctx.update_layout(get_widget_id(), state); - write_ctx.commit(); - } + // 排列子控件 + auto child = get_child(); + if (child) { + child->arrange(state); + } + } - // 排列子控件 - auto child = get_child(); - if (child) { - child->arrange(state); - } -} + int32_t custom_shader_widget_base::build_render_command( + render_collector& collector, + int32_t z_order + ) const { + // 获取布局状态 + auto layout_state_opt = get_layout_state(); + if (!layout_state_opt) { + return z_order; + } -int32_t custom_shader_widget_base::build_render_command( - render_collector& collector, - int32_t z_order -) const { - // 获取布局状态 - auto layout_state_opt = get_layout_state(); - if (!layout_state_opt) { - return z_order; - } + const auto& state = layout_state_opt.value(); - const auto& state = layout_state_opt.value(); + // 检查是否应该渲染 + if (!should_render()) { + return z_order; + } - // 检查是否应该渲染 - if (!should_render()) { - return z_order; - } + // 获取渲染模式 + auto render_mode = get_render_mode(); - // 获取渲染模式 - auto render_mode = get_render_mode(); + // TODO: post_process 模式需要特殊处理 + // post_process 模式不是先绘制子控件,而是影响当前控件下的内容 + // 这需要在渲染器层面支持,需要后续实现 shader_resource_manager 和 + // custom_shader_widget_renderer 后再完善 - // TODO: post_process 模式需要特殊处理 - // post_process 模式不是先绘制子控件,而是影响当前控件下的内容 - // 这需要在渲染器层面支持,需要后续实现 shader_resource_manager 和 - // custom_shader_widget_renderer 后再完善 + // 对于 procedural 和 custom_geometry 模式,直接构建渲染命令 + // 对于 post_process 模式,暂时使用占位符,后续完善 - // 对于 procedural 和 custom_geometry 模式,直接构建渲染命令 - // 对于 post_process 模式,暂时使用占位符,后续完善 + if (render_mode == custom_shader_render_mode::post_process) { + // post_process 模式:需要在渲染时先渲染子内容,再应用着色器效果 + // TODO: 需要实现完整的 post_process 支持 + // 包括: + // 1. 创建临时渲染目标 (offscreen target) + // 2. 先渲染子内容到临时目标 + // 3. 应用着色器效果(采样临时目标) + // 4. 释放临时目标 - if (render_mode == custom_shader_render_mode::post_process) { - // post_process 模式:需要在渲染时先渲染子内容,再应用着色器效果 - // TODO: 需要实现完整的 post_process 支持 - // 包括: - // 1. 创建临时渲染目标 (offscreen target) - // 2. 先渲染子内容到临时目标 - // 3. 应用着色器效果(采样临时目标) - // 4. 释放临时目标 + // 暂时提交一个占位符命令 + shader_uniform_map uniforms; + uniforms["_render_mode"] = static_cast(render_mode); - // 暂时提交一个占位符命令 - shader_uniform_map uniforms; - uniforms["_render_mode"] = static_cast(render_mode); + custom_shader_effect effect( + state.global_pos(), + state.size(), + get_shader_id(), + std::move(uniforms), + 1.0f + ); + effect.source = sample_source::previous_content; + effect.order = render_order::at(z_order, state.get_global_aabb()); - custom_shader_effect effect( - vec2f_t(state.offset().x(), state.offset().y()), - vec2f_t(state.size().x(), state.size().y()), - get_shader_id(), - std::move(uniforms), - 1.0f - ); - effect.source = sample_source::previous_content; - effect.order = render_order::at(z_order, state.clip_rect()); + collector.submit_post_effect(std::move(effect)); + } + else { + // procedural 或 custom_geometry 模式 + custom_shader_widget_command cmd; - collector.submit_post_effect(std::move(effect)); - } - else { - // procedural 或 custom_geometry 模式 - custom_shader_widget_command cmd; + // 基本信息 + cmd.shader_id = get_shader_id(); + cmd.position = state.global_pos(); + cmd.size = state.size(); + cmd.order = render_order::at(z_order, state.get_global_aabb()); - // 基本信息 - cmd.shader_id = get_shader_id(); - cmd.position = vec2f_t(state.offset().x(), state.offset().y()); - cmd.size = vec2f_t(state.size().x(), state.size().y()); - cmd.order = render_order::at(z_order, state.clip_rect()); + // 着色器配置 + cmd.vert_spirv = get_vertex_shader_spirv(); + cmd.frag_spirv = get_fragment_shader_spirv(); + cmd.bindings = get_bindings(); - // 着色器配置 - cmd.vert_spirv = get_vertex_shader_spirv(); - cmd.frag_spirv = get_fragment_shader_spirv(); - cmd.bindings = get_bindings(); + // 渲染模式 + cmd.render_mode = render_mode; + cmd.source_texture = get_source_texture_id(); - // 渲染模式 - cmd.render_mode = render_mode; - cmd.source_texture = get_source_texture_id(); + // 参数数据 + auto params_pair = get_params_data(); + cmd.params_data = params_pair.first; + cmd.params_size = params_pair.second; + cmd.params_dirty = is_params_dirty(); - // 参数数据 - auto params_pair = get_params_data(); - cmd.params_data = params_pair.first; - cmd.params_size = params_pair.second; - cmd.params_dirty = is_params_dirty(); + // 顶点数据(custom_geometry 模式) + cmd.vertex_config = get_vertex_buffer_config(); + cmd.index_config = get_index_buffer_config(); + cmd.vertices_dirty = is_vertices_dirty(); - // 顶点数据(custom_geometry 模式) - cmd.vertex_config = get_vertex_buffer_config(); - cmd.index_config = get_index_buffer_config(); - cmd.vertices_dirty = is_vertices_dirty(); + // 提交命令 - 使用占位符 custom_shader_effect,实际命令在渲染器中构建 + custom_shader_effect effect( + cmd.position, + cmd.size, + cmd.shader_id, + {}, + 1.0f + ); + effect.order = cmd.order; - // 提交命令 - 使用占位符 custom_shader_effect,实际命令在渲染器中构建 - custom_shader_effect effect( - cmd.position, - cmd.size, - cmd.shader_id, - {}, - 1.0f - ); - effect.order = cmd.order; + collector.submit_post_effect(std::move(effect)); + } - collector.submit_post_effect(std::move(effect)); - } + return z_order + 1; + } - return z_order + 1; -} + void custom_shader_widget_base::set_fixed_size(const vec2f_t& size) { + fixed_size_ = size; + mark_measure_dirty(); + } -void custom_shader_widget_base::set_fixed_size(const vec2f_t& size) { - fixed_size_ = size; - mark_measure_dirty(); -} + void custom_shader_widget_base::mark_render_dirty_internal() { + mark_render_dirty(); + } -void custom_shader_widget_base::mark_render_dirty_internal() { - mark_render_dirty(); -} - -auto custom_shader_widget_base::get_vertex_shader_spirv() const - -> std::span { - // 默认返回空 span,由渲染器使用默认顶点着色器 - return {}; -} - -} // namespace mirage::ui \ No newline at end of file + auto custom_shader_widget_base::get_vertex_shader_spirv() const + -> std::span { + // 默认返回空 span,由渲染器使用默认顶点着色器 + return {}; + } +} // namespace mirage diff --git a/src/ui/widgets/custom_shader/custom_shader_widget_base.h b/src/ui/widgets/custom_shader/custom_shader_widget_base.h index 2b356dc..e641ebe 100644 --- a/src/ui/widgets/custom_shader/custom_shader_widget_base.h +++ b/src/ui/widgets/custom_shader/custom_shader_widget_base.h @@ -75,6 +75,25 @@ protected: /// @brief 清除参数脏标记 virtual void clear_params_dirty() = 0; + // ======================================================================== + // Push Constants 效果参数相关(可选重写) + // ======================================================================== + + /// @brief 获取 Push Constants 效果参数数据指针和大小 + /// @return {数据指针, 数据大小},如果没有 Push Constants 则返回 {nullptr, 0} + [[nodiscard]] virtual auto get_push_constant_effect_data() const + -> std::pair { + return {nullptr, 0}; + } + + /// @brief Push Constants 效果参数是否已修改 + [[nodiscard]] virtual auto is_push_constant_effect_dirty() const noexcept -> bool { + return false; + } + + /// @brief 清除 Push Constants 效果参数脏标记 + virtual void clear_push_constant_effect_dirty() {} + // ======================================================================== // 子类可选重写的虚函数 // ======================================================================== @@ -107,7 +126,7 @@ protected: /// @brief 获取顶点缓冲配置 [[nodiscard]] virtual auto get_vertex_buffer_config() const - -> std::optional { + -> std::optional { return std::nullopt; } diff --git a/src/ui/widgets/custom_shader/custom_shader_widget_types.h b/src/ui/widgets/custom_shader/custom_shader_widget_types.h index 9455856..b9f2c0c 100644 --- a/src/ui/widgets/custom_shader/custom_shader_widget_types.h +++ b/src/ui/widgets/custom_shader/custom_shader_widget_types.h @@ -15,8 +15,9 @@ namespace mirage { custom_geometry ///< 自定义几何(使用自定义顶点和顶点着色器) }; - /// @brief 顶点缓冲配置 - struct vertex_buffer_config { + /// @brief 顶点缓冲配置(用于自定义着色器控件) + /// @note 使用 custom_vertex_buffer_config 以避免与 mirage::vertex_buffer_config 冲突 + struct custom_vertex_buffer_config { const void* data; ///< 顶点数据指针 size_t data_size; ///< 数据大小(字节) size_t vertex_count; ///< 顶点数量 @@ -40,4 +41,4 @@ namespace mirage { } return hash; } -} // namespace mirage::ui +} // namespace mirage diff --git a/src/ui/widgets/custom_shader/effects/blur_widget.h b/src/ui/widgets/custom_shader/effects/blur_widget.h index c4501f2..c51e979 100644 --- a/src/ui/widgets/custom_shader/effects/blur_widget.h +++ b/src/ui/widgets/custom_shader/effects/blur_widget.h @@ -1,9 +1,9 @@ #pragma once -#include "ui/widgets/custom_shader/custom_shader_widget.h" -#include +#include "ui/widgets/custom_shader/shader_spec_widget.h" +#include "blur_vert_frag.h" -namespace mirage::ui { +namespace mirage { /// @brief 高斯模糊效果控件 /// @@ -13,7 +13,7 @@ namespace mirage::ui { /// /// @par 参数说明 /// - radius: 模糊半径(像素),控制模糊程度 -/// - intensity: 效果强度 [0.0, 1.0],控制模糊效果的明显程度 +/// - samples: 采样次数,控制模糊质量 /// - direction: 模糊方向,用于分离式模糊(当前单 pass 实现中可忽略) /// /// @par 使用示例 @@ -22,25 +22,22 @@ namespace mirage::ui { /// auto blur = create_widget(); /// blur->set_size({200, 100}); /// blur->set_radius(5.0f); -/// blur->set_intensity(0.8f); +/// blur->set_samples(32); /// blur->set_direction(glm::vec2{1.0f, 0.0f}); // 水平模糊 /// @endcode -class blur_widget : public custom_shader_widget { +class blur_widget : public shader_spec_widget> { public: - /// @brief 模糊效果参数结构体 - /// @note 对应 shader 中的 BlurParams UBO - /// @note 结构体大小为 24 字节,符合 std140 布局对齐要求 - struct BlurParams { - float radius{5.0f}; ///< 模糊半径(像素) - float intensity{1.0f}; ///< 效果强度 [0, 1] - alignas(8) glm::vec2 direction{ ///< 模糊方向(用于分离式模糊) - 1.0f, 0.0f - }; - }; + blur_widget() { + // Uniform Buffer 参数默认值 + params().radius = 5.0f; + params().samples = 16; + params().direction = vec2f_t{1.0f, 0.0f}; - static_assert(sizeof(BlurParams) == 24, "BlurParams must be 24 bytes for std140 layout"); - - blur_widget() = default; + // Push Constants 效果参数默认值 + push_constant_effect().intensity = 1.0f; + push_constant_effect().effect_rect = vec4f_t{0.0f, 0.0f, 0.0f, 0.0f}; + push_constant_effect().original_effect_rect = vec4f_t{0.0f, 0.0f, 0.0f, 0.0f}; + } ~blur_widget() override = default; // ======================================================================== @@ -51,7 +48,7 @@ public: /// @param radius 模糊半径(像素) /// @return *this auto& set_radius(float radius) { - params().radius = glm::max(0.0f, radius); + params().radius = std::max(0.0f, radius); mark_params_dirty(); return *this; } @@ -62,25 +59,25 @@ public: return params().radius; } - /// @brief 设置效果强度 - /// @param intensity 效果强度 [0, 1] + /// @brief 设置采样次数 + /// @param samples 采样次数 /// @return *this - auto& set_intensity(float intensity) { - params().intensity = glm::clamp(intensity, 0.0f, 1.0f); + auto& set_samples(int32_t samples) { + params().samples = std::max(1, samples); mark_params_dirty(); return *this; } - /// @brief 获取效果强度 - /// @return 效果强度 [0, 1] - [[nodiscard]] auto get_intensity() const noexcept -> float { - return params().intensity; + /// @brief 获取采样次数 + /// @return 采样次数 + [[nodiscard]] auto get_samples() const noexcept -> int32_t { + return params().samples; } /// @brief 设置模糊方向 /// @param direction 模糊方向向量(归一化) /// @return *this - auto& set_direction(const glm::vec2& direction) { + auto& set_direction(const vec2f_t& direction) { params().direction = direction; mark_params_dirty(); return *this; @@ -88,14 +85,14 @@ public: /// @brief 获取模糊方向 /// @return 模糊方向向量 - [[nodiscard]] auto get_direction() const noexcept -> glm::vec2 { + [[nodiscard]] auto get_direction() const noexcept -> vec2f_t { return params().direction; } /// @brief 设置水平模糊方向 /// @return *this auto& set_horizontal() { - params().direction = glm::vec2{1.0f, 0.0f}; + params().direction = vec2f_t{1.0f, 0.0f}; mark_params_dirty(); return *this; } @@ -103,64 +100,70 @@ public: /// @brief 设置垂直模糊方向 /// @return *this auto& set_vertical() { - params().direction = glm::vec2{0.0f, 1.0f}; + params().direction = vec2f_t{0.0f, 1.0f}; mark_params_dirty(); return *this; } // ======================================================================== - // 基类虚函数实现 + // Push Constants 效果参数接口 + // ======================================================================== + + /// @brief 设置效果强度 + /// @param intensity 强度值 [0.0, 1.0] + /// @return *this + auto& set_intensity(float intensity) { + push_constant_effect().intensity = std::clamp(intensity, 0.0f, 1.0f); + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 获取效果强度 + /// @return 强度值 [0.0, 1.0] + [[nodiscard]] auto get_intensity() const noexcept -> float { + return push_constant_effect().intensity; + } + + /// @brief 设置效果区域 + /// @param rect 效果区域 (x, y, width, height) + /// @return *this + auto& set_effect_rect(const vec4f_t& rect) { + push_constant_effect().effect_rect = rect; + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 获取效果区域 + /// @return 效果区域 (x, y, width, height) + [[nodiscard]] auto get_effect_rect() const noexcept -> vec4f_t { + return push_constant_effect().effect_rect; + } + + /// @brief 设置原始效果区域(用于 UV 计算) + /// @param rect 原始效果区域 (x, y, width, height) + /// @return *this + auto& set_original_effect_rect(const vec4f_t& rect) { + push_constant_effect().original_effect_rect = rect; + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 获取原始效果区域 + /// @return 原始效果区域 (x, y, width, height) + [[nodiscard]] auto get_original_effect_rect() const noexcept -> vec4f_t { + return push_constant_effect().original_effect_rect; + } + + // ======================================================================== + // 基类虚函数实现 - 只需重写渲染模式 // ======================================================================== protected: - /// @brief 获取着色器唯一标识符 - [[nodiscard]] auto get_shader_id() const noexcept -> uint32_t override { - return hash_compile_time("blur_widget"); - } - - /// @brief 获取片段着色器 SPIR-V - /// @note TODO: 使用着色器工具链预编译的 SPIR-V - [[nodiscard]] auto get_fragment_shader_spirv() const - -> std::span override { - // TODO: 返回预编译的 blur.frag.glsl SPIR-V - // 从生成的着色器头文件获取: - // return shaders::blur_frag_spirv; - return {}; - } - - /// @brief 获取描述符绑定信息 - [[nodiscard]] auto get_bindings() const - -> std::span override { - static constexpr shader_binding_info bindings[] = { - { - .binding = 0, - .type = vk::DescriptorType::eUniformBuffer, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - }, - { - .binding = 1, - .type = vk::DescriptorType::eCombinedImageSampler, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - } - }; - return bindings; - } - /// @brief 获取渲染模式 [[nodiscard]] auto get_render_mode() const noexcept -> custom_shader_render_mode override { return custom_shader_render_mode::post_process; } - - /// @brief 获取顶点着色器 SPIR-V - /// @note 使用默认的程序化四边形顶点着色器 - [[nodiscard]] auto get_vertex_shader_spirv() const - -> std::span override { - // 返回空 span 使用默认顶点着色器 - return {}; - } }; -} // namespace mirage::ui \ No newline at end of file +} // namespace mirage \ No newline at end of file diff --git a/src/ui/widgets/custom_shader/effects/chromatic_aberration_widget.h b/src/ui/widgets/custom_shader/effects/chromatic_aberration_widget.h index cb72eba..af99a90 100644 --- a/src/ui/widgets/custom_shader/effects/chromatic_aberration_widget.h +++ b/src/ui/widgets/custom_shader/effects/chromatic_aberration_widget.h @@ -1,9 +1,9 @@ #pragma once -#include "ui/widgets/custom_shader/custom_shader_widget.h" -#include +#include "ui/widgets/custom_shader/shader_spec_widget.h" +#include "chromatic_aberration_vert_frag.h" -namespace mirage::ui { +namespace mirage { /// @brief 色差效果控件 /// @@ -12,32 +12,25 @@ namespace mirage::ui { /// /// @par 参数说明 /// - offset: RGB 通道偏移量(归一化坐标),控制颜色分离的程度 -/// - intensity: 效果强度 [0.0, 1.0],控制色差效果的明显程度 /// /// @par 使用示例 /// @code /// // 创建色差效果控件 /// auto chromatic_aberration = create_widget(); /// chromatic_aberration->set_size({200, 100}); -/// chromatic_aberration->set_offset(glm::vec2{0.02f, 0.0f}); -/// chromatic_aberration->set_intensity(0.8f); +/// chromatic_aberration->set_offset(vec2f_t{0.02f, 0.0f}); /// @endcode -class chromatic_aberration_widget : public custom_shader_widget { +class chromatic_aberration_widget : public shader_spec_widget> { public: - /// @brief 色差效果参数结构体 - /// @note 对应 shader 中的 ChromaticAberrationParams UBO - /// @note 结构体大小为 24 字节,符合 std140 布局对齐要求 - struct ChromaticAberrationParams { - alignas(8) glm::vec2 offset{ ///< RGB 通道偏移量 - 0.01f, 0.01f - }; - float intensity{1.0f}; ///< 效果强度 [0, 1] - float padding[2]{0.0f, 0.0f}; ///< 对齐填充 - }; + chromatic_aberration_widget() { + // Uniform Buffer 参数默认值 + params().offset = vec2f_t{0.02f, 0.0f}; - static_assert(sizeof(ChromaticAberrationParams) == 24, "ChromaticAberrationParams must be 24 bytes for std140 layout"); - - chromatic_aberration_widget() = default; + // Push Constants 效果参数默认值 + push_constant_effect().intensity = 1.0f; + push_constant_effect().effect_rect = vec4f_t{0.0f, 0.0f, 0.0f, 0.0f}; + push_constant_effect().original_effect_rect = vec4f_t{0.0f, 0.0f, 0.0f, 0.0f}; + } ~chromatic_aberration_widget() override = default; // ======================================================================== @@ -47,7 +40,7 @@ public: /// @brief 设置 RGB 通道偏移量 /// @param offset 偏移量(归一化坐标) /// @return *this - auto& set_offset(const glm::vec2& offset) { + auto& set_offset(const vec2f_t& offset) { params().offset = offset; mark_params_dirty(); return *this; @@ -55,7 +48,7 @@ public: /// @brief 获取 RGB 通道偏移量 /// @return 偏移量 - [[nodiscard]] auto get_offset() const noexcept -> glm::vec2 { + [[nodiscard]] auto get_offset() const noexcept -> vec2f_t { return params().offset; } @@ -63,7 +56,7 @@ public: /// @param x_offset X 轴偏移量 /// @return *this auto& set_offset_x(float x_offset) { - params().offset.x = x_offset; + params().offset.x() = x_offset; mark_params_dirty(); return *this; } @@ -71,14 +64,14 @@ public: /// @brief 获取 X 轴偏移量 /// @return X 轴偏移量 [[nodiscard]] auto get_offset_x() const noexcept -> float { - return params().offset.x; + return params().offset.x(); } /// @brief 设置 Y 轴偏移量 /// @param y_offset Y 轴偏移量 /// @return *this auto& set_offset_y(float y_offset) { - params().offset.y = y_offset; + params().offset.y() = y_offset; mark_params_dirty(); return *this; } @@ -86,77 +79,68 @@ public: /// @brief 获取 Y 轴偏移量 /// @return Y 轴偏移量 [[nodiscard]] auto get_offset_y() const noexcept -> float { - return params().offset.y; + return params().offset.y(); } + // ======================================================================== + // Push Constants 效果参数接口 + // ======================================================================== + /// @brief 设置效果强度 - /// @param intensity 效果强度 [0, 1] + /// @param intensity 强度值 [0.0, 1.0] /// @return *this auto& set_intensity(float intensity) { - params().intensity = glm::clamp(intensity, 0.0f, 1.0f); - mark_params_dirty(); + push_constant_effect().intensity = std::clamp(intensity, 0.0f, 1.0f); + mark_push_constant_effect_dirty(); return *this; } /// @brief 获取效果强度 - /// @return 效果强度 [0, 1] + /// @return 强度值 [0.0, 1.0] [[nodiscard]] auto get_intensity() const noexcept -> float { - return params().intensity; + return push_constant_effect().intensity; + } + + /// @brief 设置效果区域 + /// @param rect 效果区域 (x, y, width, height) + /// @return *this + auto& set_effect_rect(const vec4f_t& rect) { + push_constant_effect().effect_rect = rect; + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 获取效果区域 + /// @return 效果区域 (x, y, width, height) + [[nodiscard]] auto get_effect_rect() const noexcept -> vec4f_t { + return push_constant_effect().effect_rect; + } + + /// @brief 设置原始效果区域(用于 UV 计算) + /// @param rect 原始效果区域 (x, y, width, height) + /// @return *this + auto& set_original_effect_rect(const vec4f_t& rect) { + push_constant_effect().original_effect_rect = rect; + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 获取原始效果区域 + /// @return 原始效果区域 (x, y, width, height) + [[nodiscard]] auto get_original_effect_rect() const noexcept -> vec4f_t { + return push_constant_effect().original_effect_rect; } // ======================================================================== - // 基类虚函数实现 + // 基类虚函数实现 - 只需重写渲染模式 // ======================================================================== protected: - /// @brief 获取着色器唯一标识符 - [[nodiscard]] auto get_shader_id() const noexcept -> uint32_t override { - return hash_compile_time("chromatic_aberration_widget"); - } - - /// @brief 获取片段着色器 SPIR-V - /// @note TODO: 使用着色器工具链预编译的 SPIR-V - [[nodiscard]] auto get_fragment_shader_spirv() const - -> std::span override { - // TODO: 返回预编译的 chromatic_aberration.frag.glsl SPIR-V - // 从生成的着色器头文件获取: - // return shaders::chromatic_aberration_frag_spirv; - return {}; - } - - /// @brief 获取描述符绑定信息 - [[nodiscard]] auto get_bindings() const - -> std::span override { - static constexpr shader_binding_info bindings[] = { - { - .binding = 0, - .type = vk::DescriptorType::eUniformBuffer, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - }, - { - .binding = 1, - .type = vk::DescriptorType::eCombinedImageSampler, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - } - }; - return bindings; - } - /// @brief 获取渲染模式 [[nodiscard]] auto get_render_mode() const noexcept -> custom_shader_render_mode override { return custom_shader_render_mode::post_process; } - - /// @brief 获取顶点着色器 SPIR-V - /// @note 使用默认的程序化四边形顶点着色器 - [[nodiscard]] auto get_vertex_shader_spirv() const - -> std::span override { - // 返回空 span 使用默认顶点着色器 - return {}; - } }; -} // namespace mirage::ui \ No newline at end of file +} // namespace mirage \ No newline at end of file diff --git a/src/ui/widgets/custom_shader/effects/color_adjust_widget.h b/src/ui/widgets/custom_shader/effects/color_adjust_widget.h index 41a3b06..31b3add 100644 --- a/src/ui/widgets/custom_shader/effects/color_adjust_widget.h +++ b/src/ui/widgets/custom_shader/effects/color_adjust_widget.h @@ -1,9 +1,9 @@ #pragma once -#include "ui/widgets/custom_shader/custom_shader_widget.h" -#include +#include "ui/widgets/custom_shader/shader_spec_widget.h" +#include "color_adjust_vert_frag.h" -namespace mirage::ui { +namespace mirage { /// @brief 颜色调整控件 /// @@ -20,21 +20,20 @@ namespace mirage::ui { /// color_adjust->set_saturation(1.5f); /// color_adjust->set_gamma(0.8f); /// @endcode -class color_adjust_widget : public custom_shader_widget { +class color_adjust_widget : public shader_spec_widget> { public: - /// @brief 颜色调整参数结构体 - /// @note 对应 shader 中的 ColorAdjustParams UBO - /// @note 结构体大小为 32 字节,符合 std140 布局对齐要求 - struct ColorAdjustParams { - float brightness{0.0f}; ///< 亮度 [-1, 1] - float contrast{1.0f}; ///< 对比度 [0, 2] - float saturation{1.0f}; ///< 饱和度 [0, 2] - float gamma{1.0f}; ///< 伽马 [0.1, 3] - }; + color_adjust_widget() { + // Uniform Buffer 参数默认值 + params().brightness = 0.0f; + params().contrast = 1.0f; + params().saturation = 1.0f; + params().gamma = 1.0f; - static_assert(sizeof(ColorAdjustParams) == 16, "ColorAdjustParams must be 16 bytes for std140 layout"); - - color_adjust_widget() = default; + // Push Constants 效果参数默认值 + push_constant_effect().intensity = 1.0f; + push_constant_effect().effect_rect = vec4f_t{0.0f, 0.0f, 0.0f, 0.0f}; + push_constant_effect().original_effect_rect = vec4f_t{0.0f, 0.0f, 0.0f, 0.0f}; + } ~color_adjust_widget() override = default; // ======================================================================== @@ -45,7 +44,7 @@ public: /// @param brightness 亮度 [-1, 1] /// @return *this auto& set_brightness(float brightness) { - params().brightness = glm::clamp(brightness, -1.0f, 1.0f); + params().brightness = std::clamp(brightness, -1.0f, 1.0f); mark_params_dirty(); return *this; } @@ -60,7 +59,7 @@ public: /// @param contrast 对比度 [0, 2] /// @return *this auto& set_contrast(float contrast) { - params().contrast = glm::clamp(contrast, 0.0f, 2.0f); + params().contrast = std::clamp(contrast, 0.0f, 2.0f); mark_params_dirty(); return *this; } @@ -75,7 +74,7 @@ public: /// @param saturation 饱和度 [0, 2] /// @return *this auto& set_saturation(float saturation) { - params().saturation = glm::clamp(saturation, 0.0f, 2.0f); + params().saturation = std::clamp(saturation, 0.0f, 2.0f); mark_params_dirty(); return *this; } @@ -90,7 +89,7 @@ public: /// @param gamma 伽马 [0.1, 3] /// @return *this auto& set_gamma(float gamma) { - params().gamma = glm::clamp(gamma, 0.1f, 3.0f); + params().gamma = std::clamp(gamma, 0.1f, 3.0f); mark_params_dirty(); return *this; } @@ -102,58 +101,64 @@ public: } // ======================================================================== - // 基类虚函数实现 + // Push Constants 效果参数接口 + // ======================================================================== + + /// @brief 设置效果强度 + /// @param intensity 强度值 [0.0, 1.0] + /// @return *this + auto& set_intensity(float intensity) { + push_constant_effect().intensity = std::clamp(intensity, 0.0f, 1.0f); + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 获取效果强度 + /// @return 强度值 [0.0, 1.0] + [[nodiscard]] auto get_intensity() const noexcept -> float { + return push_constant_effect().intensity; + } + + /// @brief 设置效果区域 + /// @param rect 效果区域 (x, y, width, height) + /// @return *this + auto& set_effect_rect(const vec4f_t& rect) { + push_constant_effect().effect_rect = rect; + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 获取效果区域 + /// @return 效果区域 (x, y, width, height) + [[nodiscard]] auto get_effect_rect() const noexcept -> vec4f_t { + return push_constant_effect().effect_rect; + } + + /// @brief 设置原始效果区域(用于 UV 计算) + /// @param rect 原始效果区域 (x, y, width, height) + /// @return *this + auto& set_original_effect_rect(const vec4f_t& rect) { + push_constant_effect().original_effect_rect = rect; + mark_push_constant_effect_dirty(); + return *this; + } + + /// @brief 获取原始效果区域 + /// @return 原始效果区域 (x, y, width, height) + [[nodiscard]] auto get_original_effect_rect() const noexcept -> vec4f_t { + return push_constant_effect().original_effect_rect; + } + + // ======================================================================== + // 基类虚函数实现 - 只需重写渲染模式 // ======================================================================== protected: - /// @brief 获取着色器唯一标识符 - [[nodiscard]] auto get_shader_id() const noexcept -> uint32_t override { - return hash_compile_time("color_adjust_widget"); - } - - /// @brief 获取片段着色器 SPIR-V - /// @note TODO: 使用着色器工具链预编译的 SPIR-V - [[nodiscard]] auto get_fragment_shader_spirv() const - -> std::span override { - // TODO: 返回预编译的 color_adjust.frag.glsl SPIR-V - // 从生成的着色器头文件获取: - // return shaders::color_adjust_frag_spirv; - return {}; - } - - /// @brief 获取描述符绑定信息 - [[nodiscard]] auto get_bindings() const - -> std::span override { - static constexpr shader_binding_info bindings[] = { - { - .binding = 0, - .type = vk::DescriptorType::eUniformBuffer, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - }, - { - .binding = 1, - .type = vk::DescriptorType::eCombinedImageSampler, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - } - }; - return bindings; - } - /// @brief 获取渲染模式 [[nodiscard]] auto get_render_mode() const noexcept -> custom_shader_render_mode override { return custom_shader_render_mode::post_process; } - - /// @brief 获取顶点着色器 SPIR-V - /// @note 使用默认的程序化四边形顶点着色器 - [[nodiscard]] auto get_vertex_shader_spirv() const - -> std::span override { - // 返回 nullopt 使用默认顶点着色器 - return {}; - } }; -} // namespace mirage::ui \ No newline at end of file +} // namespace mirage \ No newline at end of file diff --git a/src/ui/widgets/custom_shader/effects/color_tint_widget.h b/src/ui/widgets/custom_shader/effects/color_tint_widget.h index f9dee6f..d835be3 100644 --- a/src/ui/widgets/custom_shader/effects/color_tint_widget.h +++ b/src/ui/widgets/custom_shader/effects/color_tint_widget.h @@ -1,14 +1,15 @@ #pragma once -#include "ui/widgets/custom_shader/custom_shader_widget.h" -#include +#include "ui/widgets/custom_shader/shader_spec_widget.h" +#include "color_tint_vert_frag.h" -namespace mirage::ui { +namespace mirage { /// @brief 色调映射控件 /// /// 使用后处理模式,对子控件或源纹理应用色调映射效果。 /// 支持多种混合模式:Normal、Additive、Multiply、Screen、Overlay。 +/// 效果强度通过 tint_color 的 alpha 通道控制。 /// /// @par 混合模式 /// - 0: Normal - 正常混合 @@ -22,24 +23,11 @@ namespace mirage::ui { /// // 创建色调映射控件 /// auto color_tint = create_widget(); /// color_tint->set_size({200, 100}); -/// color_tint->set_tint_color(glm::vec4{1.0f, 0.5f, 0.0f, 1.0f}); // 橙色 +/// color_tint->set_tint_color(glm::vec4{1.0f, 0.5f, 0.0f, 0.7f}); // 橙色,强度0.7 /// color_tint->set_blend_mode(2); // Multiply 模式 -/// color_tint->set_intensity(0.7f); /// @endcode -class color_tint_widget : public custom_shader_widget { +class color_tint_widget : public shader_spec_widget> { public: - /// @brief 色调映射参数结构体 - /// @note 对应 shader 中的 ColorTintParams UBO - /// @note 结构体大小为 32 字节,符合 std140 布局对齐要求 - struct ColorTintParams { - alignas(16) glm::vec4 tint_color{1.0f, 1.0f, 1.0f, 1.0f}; ///< 色调颜色 - int32_t blend_mode{0}; ///< 混合模式 - float intensity{1.0f}; ///< 效果强度 [0, 1] - float padding{0.0f}; ///< 对齐填充 - }; - - static_assert(sizeof(ColorTintParams) == 32, "ColorTintParams must be 32 bytes for std140 layout"); - color_tint_widget() = default; ~color_tint_widget() override = default; @@ -48,9 +36,9 @@ public: // ======================================================================== /// @brief 设置色调颜色 - /// @param color 色调颜色 [r, g, b, a] + /// @param color 色调颜色 [r, g, b, a],a 通道控制效果强度 /// @return *this - auto& set_tint_color(const glm::vec4& color) { + auto& set_tint_color(const color& color) { params().tint_color = color; mark_params_dirty(); return *this; @@ -58,15 +46,30 @@ public: /// @brief 获取色调颜色 /// @return 色调颜色 - [[nodiscard]] auto get_tint_color() const noexcept -> glm::vec4 { + [[nodiscard]] auto get_tint_color() const noexcept -> color { return params().tint_color; } + /// @brief 设置效果强度(通过调整 tint_color 的 alpha 通道) + /// @param intensity 强度 [0, 1] + /// @return *this + auto& set_intensity(float intensity) { + params().tint_color.a() = std::clamp(intensity, 0.0f, 1.0f); + mark_params_dirty(); + return *this; + } + + /// @brief 获取效果强度 + /// @return 强度 [0, 1] + [[nodiscard]] auto get_intensity() const noexcept -> float { + return params().tint_color.a(); + } + /// @brief 设置混合模式 /// @param mode 混合模式 (0=normal, 1=additive, 2=multiply, 3=screen, 4=overlay) /// @return *this auto& set_blend_mode(int32_t mode) { - params().blend_mode = glm::clamp(mode, 0, 4); + params().blend_mode = std::clamp(mode, 0, 4); mark_params_dirty(); return *this; } @@ -77,74 +80,16 @@ public: return params().blend_mode; } - /// @brief 设置效果强度 - /// @param intensity 强度 [0, 1] - /// @return *this - auto& set_intensity(float intensity) { - params().intensity = glm::clamp(intensity, 0.0f, 1.0f); - mark_params_dirty(); - return *this; - } - - /// @brief 获取效果强度 - /// @return 强度 [0, 1] - [[nodiscard]] auto get_intensity() const noexcept -> float { - return params().intensity; - } - // ======================================================================== - // 基类虚函数实现 + // 基类虚函数实现 - 只需重写渲染模式 // ======================================================================== protected: - /// @brief 获取着色器唯一标识符 - [[nodiscard]] auto get_shader_id() const noexcept -> uint32_t override { - return hash_compile_time("color_tint_widget"); - } - - /// @brief 获取片段着色器 SPIR-V - /// @note TODO: 使用着色器工具链预编译的 SPIR-V - [[nodiscard]] auto get_fragment_shader_spirv() const - -> std::span override { - // TODO: 返回预编译的 color_tint.frag.glsl SPIR-V - // 从生成的着色器头文件获取: - // return shaders::color_tint_frag_spirv; - return {}; - } - - /// @brief 获取描述符绑定信息 - [[nodiscard]] auto get_bindings() const - -> std::span override { - static constexpr shader_binding_info bindings[] = { - { - .binding = 0, - .type = vk::DescriptorType::eUniformBuffer, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - }, - { - .binding = 1, - .type = vk::DescriptorType::eCombinedImageSampler, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - } - }; - return bindings; - } - /// @brief 获取渲染模式 [[nodiscard]] auto get_render_mode() const noexcept -> custom_shader_render_mode override { return custom_shader_render_mode::post_process; } - - /// @brief 获取顶点着色器 SPIR-V - /// @note 使用默认的程序化四边形顶点着色器 - [[nodiscard]] auto get_vertex_shader_spirv() const - -> std::span override { - // 返回 nullopt 使用默认顶点着色器 - return {}; - } }; -} // namespace mirage::ui \ No newline at end of file +} // namespace mirage \ No newline at end of file diff --git a/src/ui/widgets/custom_shader/effects/noise_widget.h b/src/ui/widgets/custom_shader/effects/noise_widget.h index c47dc7a..9ef2581 100644 --- a/src/ui/widgets/custom_shader/effects/noise_widget.h +++ b/src/ui/widgets/custom_shader/effects/noise_widget.h @@ -1,8 +1,9 @@ #pragma once -#include "ui/widgets/custom_shader/custom_shader_widget.h" +#include "ui/widgets/custom_shader/shader_spec_widget.h" +#include "noise_vert_frag.h" -namespace mirage::ui { +namespace mirage { /// @brief 噪点效果控件 /// @@ -31,20 +32,8 @@ namespace mirage::ui { /// animated_noise->set_grain_size(2.0f); /// animated_noise->set_animated(true); // 启用动画 /// @endcode -class noise_widget : public custom_shader_widget { +class noise_widget : public shader_spec_widget> { public: - /// @brief 噪点效果参数结构体 - /// @note 对应 shader 中的 NoiseParams UBO - /// @note 结构体大小为 16 字节,符合 std140 布局对齐要求 - struct NoiseParams { - float amount{0.3f}; ///< 噪点强度 [0, 1] - float grain_size{1.5f}; ///< 颗粒大小 [0.5, 5.0] - int animated{0}; ///< 是否启用动画(0=静态,1=动态) - float time{0.0f}; ///< 时间参数(用于动画) - }; - - static_assert(sizeof(NoiseParams) == 16, "NoiseParams must be 16 bytes for std140 layout"); - noise_widget() = default; ~noise_widget() override = default; @@ -56,7 +45,7 @@ public: /// @param amount 噪点强度 [0, 1] /// @return *this auto& set_amount(float amount) { - params().amount = glm::clamp(amount, 0.0f, 1.0f); + params().amount = std::clamp(amount, 0.0f, 1.0f); mark_params_dirty(); return *this; } @@ -71,7 +60,7 @@ public: /// @param grain_size 颗粒大小 [0.5, 5.0] /// @return *this auto& set_grain_size(float grain_size) { - params().grain_size = glm::clamp(grain_size, 0.5f, 5.0f); + params().grain_size = std::clamp(grain_size, 0.5f, 5.0f); mark_params_dirty(); return *this; } @@ -104,59 +93,16 @@ public: } // ======================================================================== - // 基类虚函数实现 + // 基类虚函数实现 - 只需重写渲染模式 // ======================================================================== protected: - /// @brief 获取着色器唯一标识符 - [[nodiscard]] auto get_shader_id() const noexcept -> uint32_t override { - return hash_compile_time("noise_widget"); - } - - /// @brief 获取片段着色器 SPIR-V - /// @note TODO: 使用着色器工具链预编译的 SPIR-V - [[nodiscard]] auto get_fragment_shader_spirv() const - -> std::span override { - // TODO: 返回预编译的 noise.frag.glsl SPIR-V - // 从生成的着色器头文件获取: - // return shaders::noise_frag_spirv; - return {}; - } - - /// @brief 获取描述符绑定信息 - [[nodiscard]] auto get_bindings() const - -> std::span override { - static constexpr shader_binding_info bindings[] = { - { - .binding = 0, - .type = vk::DescriptorType::eUniformBuffer, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - }, - { - .binding = 1, - .type = vk::DescriptorType::eCombinedImageSampler, - .count = 1, - .stage = vk::ShaderStageFlagBits::eFragment - } - }; - return bindings; - } - /// @brief 获取渲染模式 [[nodiscard]] auto get_render_mode() const noexcept -> custom_shader_render_mode override { return custom_shader_render_mode::post_process; } - /// @brief 获取顶点着色器 SPIR-V - /// @note 使用默认的程序化四边形顶点着色器 - [[nodiscard]] auto get_vertex_shader_spirv() const - -> std::span override { - // 返回空 span 使用默认顶点着色器 - return {}; - } - /// @brief 检查是否需要动画更新 /// @return 当 animated 参数为 true 时返回 true [[nodiscard]] auto is_animated() const -> bool override { @@ -173,4 +119,4 @@ protected: } }; -} // namespace mirage::ui \ No newline at end of file +} // namespace mirage \ No newline at end of file diff --git a/src/ui/widgets/custom_shader/shader_spec_widget.h b/src/ui/widgets/custom_shader/shader_spec_widget.h index e3dd07e..42a9bcd 100644 --- a/src/ui/widgets/custom_shader/shader_spec_widget.h +++ b/src/ui/widgets/custom_shader/shader_spec_widget.h @@ -22,6 +22,15 @@ namespace mirage { typename T::params_type; }; + /// @brief 检测 ShaderSpec 是否提供了 push_constant_effect_type + /// + /// 如果 ShaderSpec 提供了 push_constant_effect_type,则可以使用 + /// shader_spec_widget 的 Push Constants 效果参数功能。 + template + concept has_push_constant_effect = requires { + typename T::push_constant_effect_type; + }; + /// @brief 基于 shader_spec 的自定义着色器控件模板类 /// /// @tparam ShaderSpec 着色器规格类型(由着色器工具自动生成的 xxx_shader_spec) @@ -66,12 +75,36 @@ namespace mirage { using params_type = ShaderSpec::params_type; using vertex_type = VertexT; + /// @brief 条件性定义 push_constant_effect_type + /// @note 如果 ShaderSpec 没有提供 push_constant_effect_type,则使用 void + using push_constant_effect_type = std::conditional_t< + has_push_constant_effect, + typename ShaderSpec::push_constant_effect_type, + void + >; + // 静态断言:确保 params_type 满足要求 static_assert(std::is_standard_layout_v, "params_type must be standard layout for GPU compatibility"); static_assert(alignof(params_type) <= 16, "params_type alignment must not exceed 16 bytes"); + // 静态断言:检查 Push Constants 前四个公共变量是否有效 + // 期望的顺序:projection (mat4), viewport_size (vec2), time (float), _pad0 (float) + static_assert(!ShaderSpec::has_push_constant_base_members || + (ShaderSpec::push_constant_base_member_count == 4 && + ShaderSpec::push_constant_base_member_names[0] == "projection" && + ShaderSpec::push_constant_base_member_names[1] == "viewport_size" && + ShaderSpec::push_constant_base_member_names[2] == "time" && + ShaderSpec::push_constant_base_member_names[3] == "_pad0" && + ShaderSpec::push_constant_base_member_offsets[0] == 0 && + ShaderSpec::push_constant_base_member_offsets[1] == 64 && + ShaderSpec::push_constant_base_member_offsets[2] == 72 && + ShaderSpec::push_constant_base_member_offsets[3] == 76), + "Push Constants must have exactly 4 base members: " + "projection (mat4, offset=0), viewport_size (vec2, offset=64), " + "time (float, offset=72), _pad0 (float, offset=76)"); + shader_spec_widget() = default; ~shader_spec_widget() override = default; @@ -92,6 +125,38 @@ namespace mirage { mark_render_dirty_internal(); } + // ======================================================================== + // Push Constants 效果参数访问(仅当 ShaderSpec 提供了 push_constant_effect_type 时可用) + // ======================================================================== + + /// @brief 获取 Push Constants 效果参数的可变引用 + /// @note 修改后需要调用 mark_push_constant_effect_dirty() + /// @note 仅当 ShaderSpec 提供了 push_constant_effect_type 时可用 + [[nodiscard]] auto push_constant_effect() noexcept + -> push_constant_effect_type& + requires has_push_constant_effect + { + return push_constant_effect_; + } + + /// @brief 获取 Push Constants 效果参数的只读引用 + /// @note 仅当 ShaderSpec 提供了 push_constant_effect_type 时可用 + [[nodiscard]] auto push_constant_effect() const noexcept + -> const push_constant_effect_type& + requires has_push_constant_effect + { + return push_constant_effect_; + } + + /// @brief 标记 Push Constants 效果参数已修改 + /// @note 仅当 ShaderSpec 提供了 push_constant_effect_type 时可用 + void mark_push_constant_effect_dirty() + requires has_push_constant_effect + { + push_constant_effect_dirty_ = true; + mark_render_dirty_internal(); + } + protected: // ======================================================================== // 实现基类纯虚函数 - 由 ShaderSpec 自动提供 @@ -133,6 +198,40 @@ namespace mirage { params_dirty_ = false; } + // ======================================================================== + // Push Constants 效果参数实现 + // ======================================================================== + + /// @brief 获取 Push Constants 效果参数数据指针和大小 + [[nodiscard]] auto get_push_constant_effect_data() const + -> std::pair override + { + if constexpr (has_push_constant_effect) { + return {&push_constant_effect_, sizeof(push_constant_effect_type)}; + } else { + return {nullptr, 0}; + } + } + + /// @brief 检查 Push Constants 效果参数是否已修改 + [[nodiscard]] auto is_push_constant_effect_dirty() const noexcept + -> bool override + { + if constexpr (has_push_constant_effect) { + return push_constant_effect_dirty_; + } else { + return false; + } + } + + /// @brief 清除 Push Constants 效果参数脏标志 + void clear_push_constant_effect_dirty() override + { + if constexpr (has_push_constant_effect) { + push_constant_effect_dirty_ = false; + } + } + // ======================================================================== // 自定义顶点支持 // ======================================================================== @@ -184,12 +283,12 @@ namespace mirage { /// @brief 获取顶点缓冲配置 [[nodiscard]] auto get_vertex_buffer_config() const - -> std::optional override { + -> std::optional override { if (vertices_.empty()) { return std::nullopt; } - return vertex_buffer_config{ + return custom_vertex_buffer_config{ .data = vertices_.data(), .data_size = vertices_.size() * sizeof(VertexT), .vertex_count = vertices_.size(), @@ -242,6 +341,16 @@ namespace mirage { bool params_dirty_ = true; bool vertices_dirty_ = false; + /// @brief 条件性存储 push_constant_effect_ + /// @note 使用 [[no_unique_address]] 避免 void 类型占用空间 + [[no_unique_address]] std::conditional_t< + has_push_constant_effect, + push_constant_effect_type, + std::monostate + > push_constant_effect_{}; + + bool push_constant_effect_dirty_ = true; ///< 初始为 true 以确保首次渲染 + std::vector vertices_; std::vector indices_; }; diff --git a/tools/code_generator.py b/tools/code_generator.py index 7034209..72a7524 100644 --- a/tools/code_generator.py +++ b/tools/code_generator.py @@ -22,6 +22,7 @@ from .types import ( BufferInfo, CompilationResult, MemberInfo, + PushConstantInfo, ShaderMetadata, SPIRVReflection, ToolError, @@ -76,6 +77,111 @@ def generate_member_definition(member: MemberInfo, type_map: Dict[int, TypeInfo] return result.splitlines() +# ============ Push Constants Effect Structure Generation ============ + +def generate_push_constant_effect_struct(reflection: SPIRVReflection, shader_name: str) -> str: + """生成 Push Constants 效果部分结构体定义 + + Args: + reflection: SPIR-V反射信息 + shader_name: 着色器名称(用于生成结构体名称) + + Returns: + 生成的C++结构体代码,如果没有Push Constants效果部分则返回空字符串 + """ + push_constant = reflection.push_constant + if not push_constant or not push_constant.effect_members: + return "" + + # 生成结构体名称 + struct_name = f"{shader_name.capitalize()}PushConstantEffect" + + # 准备成员信息 + members = [] + type_map = reflection.types + + for member in push_constant.effect_members: + # 计算对齐和大小 + alignment = calculate_std430_alignment(member.resolved_type, type_map) + size = calculate_std430_size(member.resolved_type, type_map) + cpp_type = spirv_type_to_cpp(member.resolved_type, type_map) + + members.append({ + 'name': member.name, + 'cpp_type': cpp_type, + 'offset': member.offset, + 'size': size, + 'alignment': alignment, + }) + + renderer = get_renderer() + context = { + 'struct_name': struct_name, + 'base_offset': push_constant.base_offset, + 'members': members, + } + + return renderer.render('push_constant/effect_struct.jinja2', context) + + +def generate_push_constant_base_layout(reflection: SPIRVReflection, shader_name: str) -> str: + """生成 Push Constants 基础部分布局信息结构体和静态检查代码 + + Args: + reflection: SPIR-V反射信息 + shader_name: 着色器名称(用于生成结构体名称) + + Returns: + 生成的C++代码,如果没有基础部分则返回空字符串 + """ + push_constant = reflection.push_constant + if not push_constant or not push_constant.base_info: + return "" + + base_info = push_constant.base_info + + # 准备模板数据 + base_members = [ + { + 'name': m.name, + 'offset': m.offset, + 'size': m.size, + 'expected_offset': m.expected_offset, + 'expected_size': m.expected_size, + 'is_valid': m.is_valid, + } + for m in base_info.members + ] + + expected_names = ['projection', 'viewport_size', 'time', '_pad0'] + present_names = [m.name for m in base_info.members] + + renderer = get_renderer() + + # 渲染布局结构体 + layout_context = { + 'struct_name': shader_name, + 'base_members': base_members, + 'expected_names': expected_names, + 'present_names': present_names, + 'total_size': base_info.total_size, + 'is_standard': base_info.is_standard_layout, + } + + layout_code = renderer.render('push_constant/base_layout.jinja2', layout_context) + + # 渲染静态检查代码 + check_context = { + 'struct_name': shader_name, + 'base_members': base_members, + 'is_standard': base_info.is_standard_layout, + } + + check_code = renderer.render('push_constant/static_check.jinja2', check_context) + + return layout_code + "\n\n" + check_code + + # ============ SPIR-V Array Generation ============ def spirv_to_cpp_array(spirv_data: bytes, symbol_name: str) -> str: @@ -314,6 +420,20 @@ def generate_header( if metadata.generate_buffer_manager and metadata.reflection and metadata.reflection.buffers: buffer_manager = generate_buffer_manager_class(metadata.reflection, metadata.name) + # 准备Push Constants效果结构体代码 + push_constant_effect_struct = "" + push_constant_effect_type_name = "void" + if metadata.reflection and metadata.reflection.push_constant and metadata.reflection.push_constant.effect_members: + push_constant_effect_struct = generate_push_constant_effect_struct(metadata.reflection, metadata.name) + push_constant_effect_type_name = f"{metadata.name.capitalize()}PushConstantEffect" + + # 准备Push Constants基础部分布局代码 + push_constant_base_layout = "" + has_push_constant_base_layout = False + if metadata.reflection and metadata.reflection.push_constant and metadata.reflection.push_constant.base_info: + push_constant_base_layout = generate_push_constant_base_layout(metadata.reflection, metadata.name) + has_push_constant_base_layout = bool(push_constant_base_layout) + # 准备SPIR-V数组和着色器类型检测 compilation_results_with_arrays = [] has_vert_shader = False @@ -350,10 +470,14 @@ def generate_header( }) # 确定参数类型名称(从 buffer 反射信息中获取) - params_type_name = f"{_to_pascal_case(metadata.name)}Params" + # 优先级:buffer > push_constant效果部分 > void + params_type_name = "void" if metadata.reflection and metadata.reflection.buffers: # 使用第一个 buffer 的结构体类型名称 params_type_name = metadata.reflection.buffers[0].struct_type.name or params_type_name + elif metadata.reflection and metadata.reflection.push_constant and metadata.reflection.push_constant.effect_members: + # 没有 buffer,但有 push_constant 效果部分,使用效果结构体名称 + params_type_name = f"{metadata.name.capitalize()}PushConstantEffect" # 使用模板渲染 renderer = get_renderer() @@ -380,6 +504,13 @@ def generate_header( 'has_frag_shader': has_frag_shader, 'vert_spirv_symbol': vert_spirv_symbol, 'frag_spirv_symbol': frag_spirv_symbol, + # Push Constants 效果结构体相关变量 + 'push_constant_effect_struct': push_constant_effect_struct, + 'push_constant_effect_type_name': push_constant_effect_type_name, + 'has_push_constant_effect': bool(push_constant_effect_struct), + # Push Constants 基础部分布局相关变量 + 'push_constant_base_layout': push_constant_base_layout, + 'has_push_constant_base_layout': has_push_constant_base_layout, } return renderer.render('base/header.jinja2', context) \ No newline at end of file diff --git a/tools/spirv_parser.py b/tools/spirv_parser.py index c733cff..07b04fa 100644 --- a/tools/spirv_parser.py +++ b/tools/spirv_parser.py @@ -8,9 +8,10 @@ SPIR-V解析模块 from __future__ import annotations import struct -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List, Optional, Tuple from .constants import * +from .type_mapping import calculate_std430_size, spirv_type_to_cpp from .types import ( ArrayTypeInfo, BaseType, @@ -19,6 +20,11 @@ from .types import ( MatrixTypeInfo, MemberInfo, PointerTypeInfo, + PUSH_CONSTANT_BASE_OFFSET, + PushConstantBaseInfo, + PushConstantBaseMember, + PushConstantBaseMemberInfo, + PushConstantInfo, ScalarTypeInfo, SPIRVReflection, StructTypeInfo, @@ -315,6 +321,9 @@ def parse_spirv_type_system(spirv_data: bytes, shader_stage: str) -> SPIRVReflec # 提取buffer信息 extract_buffers(reflection) + # 提取Push Constants信息 + reflection.push_constant = extract_push_constants(reflection) + # 提取入口点 for opcode, operands, index in instructions: if opcode == OP_ENTRY_POINT and len(operands) >= 2: @@ -414,6 +423,144 @@ def extract_buffers(reflection: SPIRVReflection): reflection.buffers.sort(key=lambda b: b.binding) +# ============ Push Constants Extraction ============ + +# 基础成员名称到标志的映射 +BASE_MEMBER_MAP = { + 'projection': (PushConstantBaseMember.PROJECTION, 0, 64), + 'viewport_size': (PushConstantBaseMember.VIEWPORT_SIZE, 64, 8), + 'time': (PushConstantBaseMember.TIME, 72, 4), + '_pad0': (PushConstantBaseMember.PAD0, 76, 4), +} + + +def _identify_base_member(member: MemberInfo, type_map: Dict[int, TypeInfo]) -> Optional[PushConstantBaseMemberInfo]: + """识别基础部分成员 + + Args: + member: 结构体成员信息 + type_map: 类型映射 + + Returns: + PushConstantBaseMemberInfo 或 None(如果不是基础部分成员) + """ + if member.offset >= PUSH_CONSTANT_BASE_OFFSET: + return None + + # 计算成员大小 + member_size = calculate_std430_size(member.resolved_type, type_map) + cpp_type = spirv_type_to_cpp(member.resolved_type, type_map) + + # 通过名称或偏移量匹配 + for name, (flag, expected_offset, expected_size) in BASE_MEMBER_MAP.items(): + if member.name == name or member.offset == expected_offset: + is_valid = (member.offset == expected_offset and member_size == expected_size) + return PushConstantBaseMemberInfo( + name=member.name, + member_flag=flag, + offset=member.offset, + size=member_size, + expected_offset=expected_offset, + expected_size=expected_size, + type_name=cpp_type, + is_valid=is_valid, + ) + + return None + + +def extract_push_constants(reflection: SPIRVReflection) -> Optional[PushConstantInfo]: + """从变量中提取Push Constants信息 + + 查找storage class为PushConstant (9)的变量,解析其结构体类型, + 并分离出效果部分(偏移量 >= 80字节的成员)。 + + Args: + reflection: SPIR-V反射信息 + + Returns: + PushConstantInfo或None(如果没有Push Constants) + """ + push_constant_var = None + + for var_id, var_info in reflection.variables.items(): + if var_info.storage_class == STORAGE_CLASS_PUSH_CONSTANT: + push_constant_var = var_info + break + + if push_constant_var is None: + return None + + var_type = reflection.types.get(push_constant_var.type_id) + if not isinstance(var_type, PointerTypeInfo): + return None + + struct_type = var_type.resolved_pointee_type + if not isinstance(struct_type, StructTypeInfo): + return None + + # 获取结构体名称 + struct_name = struct_type.name or f"PushConstant" + + # 分离基础部分和效果部分 + # 基础部分:offset < PUSH_CONSTANT_BASE_OFFSET + # 效果部分:offset >= PUSH_CONSTANT_BASE_OFFSET + base_members_info = [] + present_flags = PushConstantBaseMember.NONE + effect_members = [] + + for member in struct_type.members: + base_member_info = _identify_base_member(member, reflection.types) + if base_member_info: + # 基础部分成员 + base_members_info.append(base_member_info) + present_flags |= base_member_info.member_flag + elif member.offset >= PUSH_CONSTANT_BASE_OFFSET: + # 效果部分成员 - 计算相对于效果部分的偏移量 + relative_offset = member.offset - PUSH_CONSTANT_BASE_OFFSET + member_info = MemberInfo( + index=member.index, + name=member.name, + type_id=member.type_id, + offset=relative_offset, # 使用相对偏移量 + resolved_type=member.resolved_type, + matrix_stride=member.matrix_stride, + array_stride=member.array_stride, + is_row_major=member.is_row_major, + ) + effect_members.append(member_info) + + # 计算基础部分总大小 + base_total_size = max( + (m.offset + m.size for m in base_members_info), + default=0 + ) + + # 检查是否为标准布局(包含所有标准成员且都有效) + is_standard = ( + present_flags == PushConstantBaseMember.STANDARD and + all(m.is_valid for m in base_members_info) + ) + + # 只有存在基础部分成员时才创建 base_info + base_info = None + if base_members_info: + base_info = PushConstantBaseInfo( + members=base_members_info, + present_flags=present_flags, + total_size=base_total_size, + is_standard_layout=is_standard, + ) + + return PushConstantInfo( + name=struct_name, + struct_type=struct_type, + base_offset=PUSH_CONSTANT_BASE_OFFSET, + base_info=base_info, + effect_members=effect_members, + ) + + # ============ Legacy Interface ============ def extract_spirv_reflection(spirv_data: bytes, shader_type: str) -> Dict[str, Any]: diff --git a/tools/templates/base/header.jinja2 b/tools/templates/base/header.jinja2 index 9d84284..f767dcf 100644 --- a/tools/templates/base/header.jinja2 +++ b/tools/templates/base/header.jinja2 @@ -10,6 +10,9 @@ {%- if generate_typed_buffers and has_buffers %} #include "render/vulkan/typed_buffer.h" {%- endif %} +{%- if has_push_constant_base_layout or has_push_constant_effect %} +#include "render/uniform_types.h" +{%- endif %} {%- if namespace %} @@ -40,6 +43,24 @@ namespace {{ namespace }} { {{ typed_buffers }} {%- endif %} +{%- if has_push_constant_base_layout %} + + // ============ Push Constants 基础部分布局 ============ +// 根据 SPIR-V 反射自动生成 +// 用于编译期验证着色器与 C++ 端定义的一致性 + +{{ push_constant_base_layout }} +{%- endif %} + +{%- if push_constant_effect_struct %} + + // ============ Push Constants 效果部分结构 ============ +// 根据 SPIR-V 反射自动生成 +// 基础部分偏移量:80 字节(由渲染器自动填充) + +{{ push_constant_effect_struct }} +{%- endif %} + {%- if buffer_manager %} // ============ 缓冲区管理器类 ============ @@ -152,6 +173,16 @@ struct {{ shader_spec_name }} { return {}; {%- endif %} } + +{%- if has_push_constant_effect %} + /// @brief Push Constants 效果部分类型 + using push_constant_effect_type = {{ push_constant_effect_type_name }}; +{%- endif %} + +{%- if has_push_constant_base_layout %} + /// @brief Push Constants 基础部分布局类型 + using push_constant_base_layout = {{ shader_name }}_push_constant_base_layout; +{%- endif %} }; {%- endif %} diff --git a/tools/templates/push_constant/base_layout.jinja2 b/tools/templates/push_constant/base_layout.jinja2 new file mode 100644 index 0000000..2c73bb6 --- /dev/null +++ b/tools/templates/push_constant/base_layout.jinja2 @@ -0,0 +1,32 @@ +{# 生成 Push Constants 基础部分布局信息结构体 #} +/// @brief Push Constants 基础部分布局信息(由着色器工具自动生成) +/// +/// 此结构体描述了着色器声明的 push_constant 基础部分布局, +/// 用于编译期验证与 C++ 端定义的一致性。 +struct {{ struct_name }}_push_constant_base_layout { + // 成员存在性标志 +{%- for member in base_members %} + static constexpr bool has_{{ member.name }} = true; +{%- endfor %} +{%- for expected_name in expected_names %} +{%- if expected_name not in present_names %} + static constexpr bool has_{{ expected_name }} = false; +{%- endif %} +{%- endfor %} + + // 成员偏移量 +{%- for member in base_members %} + static constexpr size_t {{ member.name }}_offset = {{ member.offset }}; +{%- endfor %} + + // 成员大小 +{%- for member in base_members %} + static constexpr size_t {{ member.name }}_size = {{ member.size }}; +{%- endfor %} + + // 总大小 + static constexpr size_t total_size = {{ total_size }}; + + // 布局类型标识 + static constexpr bool is_standard_layout = {{ 'true' if is_standard else 'false' }}; +}; \ No newline at end of file diff --git a/tools/templates/push_constant/effect_struct.jinja2 b/tools/templates/push_constant/effect_struct.jinja2 new file mode 100644 index 0000000..b2fc8dd --- /dev/null +++ b/tools/templates/push_constant/effect_struct.jinja2 @@ -0,0 +1,10 @@ +{# 生成 Push Constants 效果部分结构体 #} +{# 结构体名称:{ShaderName}PushConstantEffect #} +// Push Constants 效果部分结构体 +// 偏移量从基础部分结束位置({{ base_offset }}字节)开始计算 +struct alignas(16) {{ struct_name }} { +{%- for member in members %} + // 偏移量:{{ member.offset }},大小:{{ member.size }},对齐方式:{{ member.alignment }} + alignas({{ member.alignment }}) {{ member.cpp_type }} {{ member.name }}; +{%- endfor %} +}; \ No newline at end of file diff --git a/tools/templates/push_constant/static_check.jinja2 b/tools/templates/push_constant/static_check.jinja2 new file mode 100644 index 0000000..02c8fb2 --- /dev/null +++ b/tools/templates/push_constant/static_check.jinja2 @@ -0,0 +1,42 @@ +{# 生成 Push Constants 静态检查代码 #} +// ============ Push Constants 静态验证 ============ +// 编译期验证着色器声明的基础部分与 C++ 端定义一致 + +{% if is_standard %} +// 验证标准布局 +static_assert( + {{ struct_name }}_push_constant_base_layout::has_projection && + {{ struct_name }}_push_constant_base_layout::projection_offset == + mirage::expected_push_constant_base_layout::projection_offset, + "{{ struct_name }}: projection offset mismatch" +); + +static_assert( + {{ struct_name }}_push_constant_base_layout::has_viewport_size && + {{ struct_name }}_push_constant_base_layout::viewport_size_offset == + mirage::expected_push_constant_base_layout::viewport_size_offset, + "{{ struct_name }}: viewport_size offset mismatch" +); + +static_assert( + {{ struct_name }}_push_constant_base_layout::has_time && + {{ struct_name }}_push_constant_base_layout::time_offset == + mirage::expected_push_constant_base_layout::time_offset, + "{{ struct_name }}: time offset mismatch" +); + +static_assert( + {{ struct_name }}_push_constant_base_layout::total_size >= + mirage::expected_push_constant_base_layout::total_size, + "{{ struct_name }}: base layout size too small" +); +{% else %} +// 非标准布局 - 自定义验证(仅验证存在的成员) +{%- for member in base_members %} +{%- if not member.is_valid %} +// WARNING: {{ member.name }} layout mismatch +// Expected: offset={{ member.expected_offset }}, size={{ member.expected_size }} +// Actual: offset={{ member.offset }}, size={{ member.size }} +{%- endif %} +{%- endfor %} +{% endif %} \ No newline at end of file diff --git a/tools/types.py b/tools/types.py index b97979a..ddc2ef1 100644 --- a/tools/types.py +++ b/tools/types.py @@ -8,7 +8,7 @@ from __future__ import annotations from dataclasses import dataclass, field -from enum import Enum +from enum import Enum, Flag, auto from pathlib import Path from typing import Any, Dict, List, Optional @@ -18,6 +18,73 @@ class ToolError(RuntimeError): pass +# ============ Push Constant Base Members ============ + +class PushConstantBaseMember(Flag): + """Push Constants 基础部分成员标志""" + NONE = 0 + PROJECTION = auto() # mat4 projection + VIEWPORT_SIZE = auto() # vec2 viewport_size + TIME = auto() # float time + PAD0 = auto() # float _pad0 + + # 预定义的组合 + STANDARD = PROJECTION | VIEWPORT_SIZE | TIME | PAD0 + VERTEX_ONLY = PROJECTION + POST_EFFECT = PROJECTION | VIEWPORT_SIZE | TIME | PAD0 + + +@dataclass +class PushConstantBaseMemberInfo: + """Push Constants 基础部分单个成员信息""" + name: str # 成员名称(如 "projection") + member_flag: PushConstantBaseMember # 成员标志 + offset: int # 实际偏移量 + size: int # 实际大小 + expected_offset: int # 期望偏移量 + expected_size: int # 期望大小 + type_name: str # C++ 类型名称 + is_valid: bool = True # 是否与期望匹配 + + +@dataclass +class PushConstantBaseInfo: + """Push Constants 基础部分完整信息""" + members: List[PushConstantBaseMemberInfo] = field(default_factory=list) + present_flags: PushConstantBaseMember = PushConstantBaseMember.NONE + total_size: int = 0 + is_standard_layout: bool = False # 是否为标准布局 + + # 期望布局定义 + EXPECTED_LAYOUT = { + 'projection': (0, 64), # (offset, size) + 'viewport_size': (64, 8), + 'time': (72, 4), + '_pad0': (76, 4), + } + EXPECTED_TOTAL_SIZE = 80 + + @property + def has_projection(self) -> bool: + """检查是否包含 projection 成员""" + return any(m.name == 'projection' for m in self.members) + + @property + def has_viewport_size(self) -> bool: + """检查是否包含 viewport_size 成员""" + return any(m.name == 'viewport_size' for m in self.members) + + @property + def has_time(self) -> bool: + """检查是否包含 time 成员""" + return any(m.name == 'time' for m in self.members) + + @property + def has_pad0(self) -> bool: + """检查是否包含 _pad0 成员""" + return any(m.name == '_pad0' for m in self.members) + + # ============ Enums ============ class StorageClass(Enum): @@ -164,6 +231,24 @@ class BindingInfo: count: int = 1 +# Push Constants 基础部分偏移量常量(字节) +# mat4 projection = 64 字节 +# vec2 viewport_size = 8 字节 +# float time = 4 字节 +# float _pad0 = 4 字节 +PUSH_CONSTANT_BASE_OFFSET = 80 + + +@dataclass +class PushConstantInfo: + """Push Constants 信息""" + name: str # 结构体名称 + struct_type: StructTypeInfo # 完整结构体类型 + base_offset: int = PUSH_CONSTANT_BASE_OFFSET # 基础部分结束的偏移量(字节) + base_info: Optional[PushConstantBaseInfo] = None # 基础部分信息 + effect_members: List[MemberInfo] = field(default_factory=list) # 效果部分的成员列表 + + @dataclass class SPIRVReflection: """完整的SPIR-V反射信息""" @@ -175,6 +260,7 @@ class SPIRVReflection: member_decorations: Dict[int, Dict[int, Dict[int, Any]]] = field(default_factory=dict) constants: Dict[int, Any] = field(default_factory=dict) buffers: List[BufferInfo] = field(default_factory=list) + push_constant: Optional[PushConstantInfo] = None # Push Constants 信息 entry_point: str = "main" shader_stage: str = "compute"