From 5a8d62f841f62710e24ad2556aa770d8bd62c19f Mon Sep 17 00:00:00 2001 From: daiqingshuang Date: Thu, 25 Dec 2025 21:04:39 +0800 Subject: [PATCH] Refactor Push Constants and Add Dual Stage Support - Removed legacy push constant structures and functions for better clarity and maintainability. - Introduced new `text_push_constants_t` structure for text rendering with optimized layout. - Implemented dual stage push constant analysis to support separate layouts for vertex and fragment shaders. - Added functions to generate push constant structures and fill functions based on shader reflection. - Enhanced static checks for push constant layouts to ensure compatibility and correctness. - Updated templates to accommodate new dual stage push constant generation. - Added support detection for procedural vertex shaders based on push constant layout. --- example/test_thread/main.cpp | 4 +- shaders/post_effect/blur.frag.glsl | 24 +- .../chromatic_aberration.frag.glsl | 18 +- shaders/post_effect/color_adjust.frag.glsl | 18 +- shaders/post_effect/color_tint.frag.glsl | 18 +- shaders/post_effect/noise.frag.glsl | 20 +- shaders/post_effect/vignette.frag.glsl | 18 +- shaders/widget/glitch_effect.frag.glsl | 22 +- shaders/widget/wave.frag.glsl | 24 +- src/render/push_constant_traits.h | 298 ++++++++++++++++ src/render/render_command.h | 124 ++++++- .../custom_shader_widget_renderer.cpp | 113 +++++- .../renderers/custom_shader_widget_renderer.h | 24 +- src/render/renderers/geometry_renderer.cpp | 13 +- src/render/renderers/text_renderer.cpp | 11 +- src/render/uniform_types.h | 198 +++-------- tools/code_generator.py | 327 ++++++++++++++++++ tools/spirv_parser.py | 193 ++++++++++- tools/templates/base/header.jinja2 | 5 + .../templates/push_constant/dual_stage.jinja2 | 199 +++++++++++ .../push_constant/static_check.jinja2 | 28 +- tools/types.py | 87 +++++ 22 files changed, 1537 insertions(+), 249 deletions(-) create mode 100644 tools/templates/push_constant/dual_stage.jinja2 diff --git a/example/test_thread/main.cpp b/example/test_thread/main.cpp index a0b053d..690a959 100644 --- a/example/test_thread/main.cpp +++ b/example/test_thread/main.cpp @@ -63,9 +63,9 @@ int main(int argc, char* argv[]) { new_widget()[ new_widget()->texture_id(texture_id).source_size(tex_size).scale(scale_mode::contain), // 圆形遮罩 + 模糊效果 (使用新的 blur_widget) - new_widget()[ + // new_widget()[ new_widget()->set_radius(40.0f) - ] | align(alignment::center_left) + // ] | align(alignment::center_left) ] // new_widget()[ // new_widget()->texture_id(texture_id).source_size(tex_size).scale(scale_mode::contain), diff --git a/shaders/post_effect/blur.frag.glsl b/shaders/post_effect/blur.frag.glsl index 8557e89..37ada9e 100644 --- a/shaders/post_effect/blur.frag.glsl +++ b/shaders/post_effect/blur.frag.glsl @@ -5,17 +5,24 @@ // ============================================================================ // ============================================================================ -// Push Constants (简化布局) +// Push Constants(双阶段布局) +// ============================================================================ +// +// 布局说明 (128 bytes): +// [0-15] Header: scale + translate (顶点着色器使用) +// [16-51] 顶点阶段 Custom: effect_rect(16) + original_effect_rect(16) + intensity(4) (顶点着色器使用) +// [52-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// // ============================================================================ layout(push_constant) uniform PushConstants { - // 基础变换 (16 bytes) [offset 0-15] + // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // 用户自定义数据 (112 bytes) [offset 16-127] - float intensity; ///< 效果强度 - vec2 viewport_size; ///< 视口大小(用于 UV 计算) + // === 片元阶段专用参数 (从 offset 52 开始) === + // 注意:跳过顶点阶段的 effect_rect(16) + original_effect_rect(16) + intensity(4) = 36 bytes [offset 16-51] + layout(offset = 52) float intensity; ///< [52-55] 效果强度 // 可扩展更多用户数据... } pc; @@ -27,9 +34,8 @@ layout(set = 0, binding = 1) uniform BlurParams { vec2 direction; ///< 模糊方向(用于分离式模糊) } blur; -// 来自顶点着色器 +// 来自顶点着色器(使用 post_process 模式的标准顶点着色器) layout(location = 0) in vec2 v_uv; -layout(location = 1) in vec2 v_screen_pos; // 输出 layout(location = 0) out vec4 frag_color; @@ -47,8 +53,8 @@ void main() { vec2 tex_size = vec2(textureSize(u_source_texture, 0)); vec2 texel_size = 1.0 / tex_size; - // 将 UV 转换为源纹理坐标 - vec2 source_uv = v_screen_pos / pc.viewport_size; + // 使用 v_uv 计算源纹理坐标(v_uv 范围 [0,1],viewport_size 转换为纹理坐标) + vec2 source_uv = v_uv; vec4 result = vec4(0.0); float total_weight = 0.0; diff --git a/shaders/post_effect/chromatic_aberration.frag.glsl b/shaders/post_effect/chromatic_aberration.frag.glsl index aea1949..555bc02 100644 --- a/shaders/post_effect/chromatic_aberration.frag.glsl +++ b/shaders/post_effect/chromatic_aberration.frag.glsl @@ -5,17 +5,25 @@ // ============================================================================ // ============================================================================ -// Push Constants (简化布局) +// Push Constants(双阶段布局) +// ============================================================================ +// +// 布局说明 (128 bytes): +// [0-15] Header: scale + translate (顶点着色器使用) +// [16-51] 顶点阶段 Custom: effect_rect(16) + original_effect_rect(16) + intensity(4) (顶点着色器使用) +// [52-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// // ============================================================================ layout(push_constant) uniform PushConstants { - // 基础变换 (16 bytes) [offset 0-15] + // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // 用户自定义数据 (112 bytes) [offset 16-127] - float intensity; ///< 效果强度 - vec2 viewport_size; ///< 视口大小(用于 UV 计算) + // === 片元阶段专用参数 (从 offset 52 开始) === + // 注意:跳过顶点阶段的 effect_rect(16) + original_effect_rect(16) + intensity(4) = 36 bytes [offset 16-51] + layout(offset = 52) float intensity; ///< [52-55] 效果强度 + vec2 viewport_size; ///< [56-63] 视口大小(用于 UV 计算) // 可扩展更多用户数据... } pc; diff --git a/shaders/post_effect/color_adjust.frag.glsl b/shaders/post_effect/color_adjust.frag.glsl index 6b05027..25d3f65 100644 --- a/shaders/post_effect/color_adjust.frag.glsl +++ b/shaders/post_effect/color_adjust.frag.glsl @@ -5,17 +5,25 @@ // ============================================================================ // ============================================================================ -// Push Constants (简化布局) +// Push Constants(双阶段布局) +// ============================================================================ +// +// 布局说明 (128 bytes): +// [0-15] Header: scale + translate (顶点着色器使用) +// [16-51] 顶点阶段 Custom: effect_rect(16) + original_effect_rect(16) + intensity(4) (顶点着色器使用) +// [52-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// // ============================================================================ layout(push_constant) uniform PushConstants { - // 基础变换 (16 bytes) [offset 0-15] + // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // 用户自定义数据 (112 bytes) [offset 16-127] - float intensity; ///< 效果强度 - vec2 viewport_size; ///< 视口大小(用于 UV 计算) + // === 片元阶段专用参数 (从 offset 52 开始) === + // 注意:跳过顶点阶段的 effect_rect(16) + original_effect_rect(16) + intensity(4) = 36 bytes [offset 16-51] + layout(offset = 52) float intensity; ///< [52-55] 效果强度 + vec2 viewport_size; ///< [56-63] 视口大小(用于 UV 计算) // 可扩展更多用户数据... } pc; diff --git a/shaders/post_effect/color_tint.frag.glsl b/shaders/post_effect/color_tint.frag.glsl index 8cfdccb..2204ef4 100644 --- a/shaders/post_effect/color_tint.frag.glsl +++ b/shaders/post_effect/color_tint.frag.glsl @@ -5,17 +5,25 @@ // ============================================================================ // ============================================================================ -// Push Constants (简化布局) +// Push Constants(双阶段布局) +// ============================================================================ +// +// 布局说明 (128 bytes): +// [0-15] Header: scale + translate (顶点着色器使用) +// [16-51] 顶点阶段 Custom: effect_rect(16) + original_effect_rect(16) + intensity(4) (顶点着色器使用) +// [52-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// // ============================================================================ layout(push_constant) uniform PushConstants { - // 基础变换 (16 bytes) [offset 0-15] + // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // 用户自定义数据 (112 bytes) [offset 16-127] - float intensity; ///< 效果强度 - vec2 viewport_size; ///< 视口大小(用于 UV 计算) + // === 片元阶段专用参数 (从 offset 52 开始) === + // 注意:跳过顶点阶段的 effect_rect(16) + original_effect_rect(16) + intensity(4) = 36 bytes [offset 16-51] + layout(offset = 52) float intensity; ///< [52-55] 效果强度 + vec2 viewport_size; ///< [56-63] 视口大小(用于 UV 计算) // 可扩展更多用户数据... } pc; diff --git a/shaders/post_effect/noise.frag.glsl b/shaders/post_effect/noise.frag.glsl index d2fb0b8..5b8f33a 100644 --- a/shaders/post_effect/noise.frag.glsl +++ b/shaders/post_effect/noise.frag.glsl @@ -5,18 +5,26 @@ // ============================================================================ // ============================================================================ -// Push Constants (简化布局) +// Push Constants(双阶段布局) +// ============================================================================ +// +// 布局说明 (128 bytes): +// [0-15] Header: scale + translate (顶点着色器使用) +// [16-51] 顶点阶段 Custom: effect_rect(16) + original_effect_rect(16) + intensity(4) (顶点着色器使用) +// [52-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// // ============================================================================ layout(push_constant) uniform PushConstants { - // 基础变换 (16 bytes) [offset 0-15] + // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // 用户自定义数据 (112 bytes) [offset 16-127] - float intensity; ///< 效果强度 - vec2 viewport_size; ///< 视口大小(用于 UV 计算) - float time; ///< 时间(用于动画噪点) + // === 片元阶段专用参数 (从 offset 52 开始) === + // 注意:跳过顶点阶段的 effect_rect(16) + original_effect_rect(16) + intensity(4) = 36 bytes [offset 16-51] + layout(offset = 52) float intensity; ///< [52-55] 效果强度 + vec2 viewport_size; ///< [56-63] 视口大小(用于 UV 计算) + float time; ///< [64-67] 时间(用于动画噪点) // 可扩展更多用户数据... } pc; diff --git a/shaders/post_effect/vignette.frag.glsl b/shaders/post_effect/vignette.frag.glsl index e1a4e0c..f9b1d3e 100644 --- a/shaders/post_effect/vignette.frag.glsl +++ b/shaders/post_effect/vignette.frag.glsl @@ -5,17 +5,25 @@ // ============================================================================ // ============================================================================ -// Push Constants (简化布局) +// Push Constants(双阶段布局) +// ============================================================================ +// +// 布局说明 (128 bytes): +// [0-15] Header: scale + translate (顶点着色器使用) +// [16-51] 顶点阶段 Custom: effect_rect(16) + original_effect_rect(16) + intensity(4) (顶点着色器使用) +// [52-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// // ============================================================================ layout(push_constant) uniform PushConstants { - // 基础变换 (16 bytes) [offset 0-15] + // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // 用户自定义数据 (112 bytes) [offset 16-127] - float intensity; ///< 效果强度 - vec2 viewport_size; ///< 视口大小(用于 UV 计算) + // === 片元阶段专用参数 (从 offset 52 开始) === + // 注意:跳过顶点阶段的 effect_rect(16) + original_effect_rect(16) + intensity(4) = 36 bytes [offset 16-51] + layout(offset = 52) float intensity; ///< [52-55] 效果强度 + vec2 viewport_size; ///< [56-63] 视口大小(用于 UV 计算) // 可扩展更多用户数据... } pc; diff --git a/shaders/widget/glitch_effect.frag.glsl b/shaders/widget/glitch_effect.frag.glsl index 3e8bad2..06c21e9 100644 --- a/shaders/widget/glitch_effect.frag.glsl +++ b/shaders/widget/glitch_effect.frag.glsl @@ -18,19 +18,27 @@ */ // ============================================================================ -// Push Constants(简化布局) +// Push Constants(双阶段布局) +// ============================================================================ +// +// 布局说明 (128 bytes): +// [0-15] Header: scale + translate (顶点着色器使用) +// [16-31] 顶点阶段 Custom: widget_pos + widget_size (顶点着色器使用) +// [32-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// // ============================================================================ layout(push_constant) uniform PushConstants { - // 基础变换 (16 bytes) [offset 0-15] + // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // 用户自定义数据 (112 bytes) [offset 16-127] - float intensity; ///< 故障强度 - float scan_line_freq; ///< 扫描线频率 - float noise_amount; ///< 噪声量 - float time; ///< 时间(用于动画效果) + // === 片元阶段专用参数 (从 offset 32 开始) === + // 注意:跳过顶点阶段的 widget_pos(8) + widget_size(8) = 16 bytes [offset 16-31] + layout(offset = 32) float intensity; ///< [32-35] 故障强度 + float scan_line_freq; ///< [36-39] 扫描线频率 + float noise_amount; ///< [40-43] 噪声量 + float time; ///< [44-47] 时间(用于动画效果) // 可扩展更多用户数据... } pc; diff --git a/shaders/widget/wave.frag.glsl b/shaders/widget/wave.frag.glsl index cd722a8..fb3ac56 100644 --- a/shaders/widget/wave.frag.glsl +++ b/shaders/widget/wave.frag.glsl @@ -18,20 +18,28 @@ */ // ============================================================================ -// Push Constants(简化布局) +// Push Constants(双阶段布局) +// ============================================================================ +// +// 布局说明 (128 bytes): +// [0-15] Header: scale + translate (顶点着色器使用) +// [16-31] 顶点阶段 Custom: widget_pos + widget_size (顶点着色器使用) +// [32-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// // ============================================================================ layout(push_constant) uniform PushConstants { - // 基础变换 (16 bytes) [offset 0-15] + // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // 用户自定义数据 (112 bytes) [offset 16-127] - vec4 base_color; ///< 次色/结束颜色 (r, g, b, a) - float frequency; ///< 波浪频率 - float amplitude; ///< 波浪振幅 - float speed; ///< 波浪速度 - float time; ///< 时间(用于动画效果) + // === 片元阶段专用参数 (从 offset 32 开始) === + // 注意:跳过顶点阶段的 widget_pos(8) + widget_size(8) = 16 bytes [offset 16-31] + layout(offset = 32) vec4 base_color; ///< [32-47] 次色/结束颜色 (r, g, b, a) + float frequency; ///< [48-51] 波浪频率 + float amplitude; ///< [52-55] 波浪振幅 + float speed; ///< [56-59] 波浪速度 + float time; ///< [60-63] 时间(用于动画效果) // 可扩展更多用户数据... } pc; diff --git a/src/render/push_constant_traits.h b/src/render/push_constant_traits.h index a8226d4..4b3754b 100644 --- a/src/render/push_constant_traits.h +++ b/src/render/push_constant_traits.h @@ -51,6 +51,304 @@ namespace mirage { requires alignof(CustomParams) <= 16; }; + // ============================================================================ + // 双阶段 Push Constant 类型检测 Concepts + // ============================================================================ + + /** + * @brief Concept: 检测类型是否提供 layout_offset 静态成员 + * + * 双阶段 Push Constant 结构体必须提供此成员以指示数据在 Custom 区域内的偏移量。 + */ + template + concept has_layout_offset = requires { + { T::layout_offset } -> std::convertible_to; + }; + + /** + * @brief Concept: 检测类型是否为有效的阶段 Push Constant 类型 + * + * 验证要求: + * - 满足 valid_custom_push_constant 的所有要求 + * - 提供 layout_offset 静态成员 + */ + template + concept valid_stage_push_constant = + valid_custom_push_constant && + has_layout_offset; + + /** + * @brief Concept: 检测 ShaderSpec 是否具有双阶段布局 + * + * 双阶段布局的 ShaderSpec 必须提供: + * - vertex_push_constant_type 类型别名 + * - fragment_push_constant_type 类型别名 + * - PushConstantLayout 嵌套类型 + */ + template + concept has_dual_stage_layout = requires { + typename ShaderSpec::vertex_push_constant_type; + typename ShaderSpec::fragment_push_constant_type; + typename ShaderSpec::PushConstantLayout; + }; + + /** + * @brief Concept: 检测 ShaderSpec 是否仅有顶点阶段布局 + */ + template + concept has_vertex_only_layout = requires { + typename ShaderSpec::vertex_push_constant_type; + typename ShaderSpec::PushConstantLayout; + } && !requires { + typename ShaderSpec::fragment_push_constant_type; + }; + + /** + * @brief Concept: 检测 ShaderSpec 是否仅有片元阶段布局 + */ + template + concept has_fragment_only_layout = requires { + typename ShaderSpec::fragment_push_constant_type; + typename ShaderSpec::PushConstantLayout; + } && !requires { + typename ShaderSpec::vertex_push_constant_type; + }; + + /** + * @brief Concept: 检测 ShaderSpec 是否使用共享布局模式 + */ + template + concept has_shared_layout = requires { + typename ShaderSpec::PushConstantLayout; + { ShaderSpec::PushConstantLayout::is_shared_layout } -> std::convertible_to; + } && ShaderSpec::PushConstantLayout::is_shared_layout; + + // ============================================================================ + // 双阶段 Push Constant 布局验证器 + // ============================================================================ + + /** + * @brief 双阶段 Push Constant 布局验证器 + * + * 验证顶点和片元阶段的 Push Constant 数据不会重叠。 + * 对于分离布局模式,片元数据的起始偏移量必须 >= 顶点数据的结束偏移量。 + * + * @tparam VertexPushConstant 顶点阶段 Push Constant 类型 + * @tparam FragmentPushConstant 片元阶段 Push Constant 类型 + * + * 使用示例: + * @code + * using Validator = push_constant_layout_validator< + * MyShaderVertexPushConstant, + * MyShaderFragmentPushConstant + * >; + * static_assert(Validator::is_valid, "Push constant regions overlap!"); + * @endcode + */ + template + struct push_constant_layout_validator { + /// 顶点数据在 Custom 区域内的起始偏移量(相对于 Custom 区域开始) + static constexpr size_t vertex_start_offset = + has_layout_offset ? VertexPushConstant::layout_offset : 0; + + /// 顶点数据的大小 + static constexpr size_t vertex_size = sizeof(VertexPushConstant); + + /// 顶点数据的结束偏移量 = 起始偏移 + 大小 + static constexpr size_t vertex_end_offset = vertex_start_offset + vertex_size; + + /// 片元数据在 Custom 区域内的起始偏移量(相对于 Custom 区域开始) + static constexpr size_t fragment_start_offset = + has_layout_offset ? FragmentPushConstant::layout_offset : 0; + + /// 片元数据的大小 + static constexpr size_t fragment_size = sizeof(FragmentPushConstant); + + /// 片元数据的结束偏移量 + static constexpr size_t fragment_end_offset = fragment_start_offset + fragment_size; + + /// 验证结果:片元起始偏移 >= 顶点结束偏移(无重叠) + static constexpr bool is_valid = (fragment_start_offset >= vertex_end_offset); + + /// 顶点数据在完整 Push Constants 中的绝对偏移量 + static constexpr size_t vertex_absolute_offset = + PUSH_CONSTANT_CUSTOM_OFFSET + vertex_start_offset; + + /// 片元数据在完整 Push Constants 中的绝对偏移量 + static constexpr size_t fragment_absolute_offset = + PUSH_CONSTANT_CUSTOM_OFFSET + fragment_start_offset; + + // 静态断言:确保布局有效 + static_assert(is_valid, + "Fragment push constant offset must be >= vertex push constant end offset. " + "Data regions would overlap! " + "Check that fragment_start_offset >= vertex_start_offset + sizeof(VertexPushConstant)."); + + // 静态断言:确保数据不超出 Custom 区域 + static_assert(vertex_end_offset <= PUSH_CONSTANT_CUSTOM_MAX_SIZE, + "Vertex push constant data exceeds custom region bounds (112 bytes)."); + static_assert(fragment_end_offset <= PUSH_CONSTANT_CUSTOM_MAX_SIZE, + "Fragment push constant data exceeds custom region bounds (112 bytes)."); + }; + + /** + * @brief 单阶段 Push Constant 布局验证器(仅顶点) + */ + template + struct vertex_only_layout_validator { + static constexpr size_t vertex_start_offset = + has_layout_offset ? VertexPushConstant::layout_offset : 0; + static constexpr size_t vertex_size = sizeof(VertexPushConstant); + static constexpr size_t vertex_end_offset = vertex_start_offset + vertex_size; + + static constexpr bool is_valid = (vertex_end_offset <= PUSH_CONSTANT_CUSTOM_MAX_SIZE); + + static_assert(is_valid, + "Vertex push constant data exceeds custom region bounds (112 bytes)."); + }; + + /** + * @brief 单阶段 Push Constant 布局验证器(仅片元) + */ + template + struct fragment_only_layout_validator { + static constexpr size_t fragment_start_offset = + has_layout_offset ? FragmentPushConstant::layout_offset : 0; + static constexpr size_t fragment_size = sizeof(FragmentPushConstant); + static constexpr size_t fragment_end_offset = fragment_start_offset + fragment_size; + + static constexpr bool is_valid = (fragment_end_offset <= PUSH_CONSTANT_CUSTOM_MAX_SIZE); + + static_assert(is_valid, + "Fragment push constant data exceeds custom region bounds (112 bytes)."); + }; + + // ============================================================================ + // ShaderSpec 布局验证辅助函数 + // ============================================================================ + + /** + * @brief 验证 ShaderSpec 的 Push Constant 布局 + * + * 根据 ShaderSpec 的布局类型自动选择适当的验证器: + * - 双阶段布局:验证顶点和片元数据不重叠 + * - 仅顶点布局:验证顶点数据不超出边界 + * - 仅片元布局:验证片元数据不超出边界 + * - 其他情况:始终返回 true + * + * @tparam ShaderSpec 着色器规格类型 + * @return true 如果布局有效 + * + * 使用示例: + * @code + * static_assert(validate_push_constant_layout(), + * "Invalid push constant layout!"); + * @endcode + */ + template + constexpr bool validate_push_constant_layout() { + if constexpr (has_dual_stage_layout) { + // 双阶段布局:使用完整验证器 + using Validator = push_constant_layout_validator< + typename ShaderSpec::vertex_push_constant_type, + typename ShaderSpec::fragment_push_constant_type + >; + return Validator::is_valid; + } + else if constexpr (has_vertex_only_layout) { + // 仅顶点布局 + using Validator = vertex_only_layout_validator< + typename ShaderSpec::vertex_push_constant_type + >; + return Validator::is_valid; + } + else if constexpr (has_fragment_only_layout) { + // 仅片元布局 + using Validator = fragment_only_layout_validator< + typename ShaderSpec::fragment_push_constant_type + >; + return Validator::is_valid; + } + else { + // 无特殊布局要求,始终有效 + return true; + } + } + + /** + * @brief 获取顶点 Push Constant 的绝对偏移量 + * + * @tparam ShaderSpec 着色器规格类型 + * @return 顶点数据在完整 Push Constants 中的绝对偏移量(字节) + */ + template + constexpr size_t get_vertex_push_constant_offset() { + if constexpr (has_dual_stage_layout || has_vertex_only_layout) { + using VPC = typename ShaderSpec::vertex_push_constant_type; + constexpr size_t relative_offset = + has_layout_offset ? VPC::layout_offset : 0; + return PUSH_CONSTANT_CUSTOM_OFFSET + relative_offset; + } + else { + return PUSH_CONSTANT_CUSTOM_OFFSET; + } + } + + /** + * @brief 获取片元 Push Constant 的绝对偏移量 + * + * @tparam ShaderSpec 着色器规格类型 + * @return 片元数据在完整 Push Constants 中的绝对偏移量(字节) + */ + template + constexpr size_t get_fragment_push_constant_offset() { + if constexpr (has_dual_stage_layout || has_fragment_only_layout) { + using FPC = typename ShaderSpec::fragment_push_constant_type; + constexpr size_t relative_offset = + has_layout_offset ? FPC::layout_offset : 0; + return PUSH_CONSTANT_CUSTOM_OFFSET + relative_offset; + } + else { + return PUSH_CONSTANT_CUSTOM_OFFSET; + } + } + + // ============================================================================ + // Push Constant 填充辅助类型 + // ============================================================================ + + /** + * @brief 双阶段 Push Constant 数据容器 + * + * 用于在运行时存储顶点和片元阶段的 Push Constant 数据。 + * 提供类型安全的数据存储和访问。 + * + * @tparam VertexPushConstant 顶点阶段 Push Constant 类型 + * @tparam FragmentPushConstant 片元阶段 Push Constant 类型 + */ + template + struct dual_stage_push_constant_data { + using vertex_type = VertexPushConstant; + using fragment_type = FragmentPushConstant; + using validator = push_constant_layout_validator; + + // 编译期验证布局 + static_assert(validator::is_valid, "Invalid dual-stage push constant layout"); + + VertexPushConstant vertex_data{}; + FragmentPushConstant fragment_data{}; + + /// 获取顶点数据在 Custom 区域内的偏移量 + static constexpr size_t vertex_custom_offset() { + return validator::vertex_start_offset; + } + + /// 获取片元数据在 Custom 区域内的偏移量 + static constexpr size_t fragment_custom_offset() { + return validator::fragment_start_offset; + } + }; + // ============================================================================ // 布局一致性静态断言 // ============================================================================ diff --git a/src/render/render_command.h b/src/render/render_command.h index 7b2de2d..1d34dc7 100644 --- a/src/render/render_command.h +++ b/src/render/render_command.h @@ -244,10 +244,54 @@ namespace mirage { // Push Constants 用户自定义数据 // 新布局: [0-15] Header (uScale + uTranslate), [16-127] Custom (用户数据) // ======================================================================== + // + // 支持两种模式: + // 1. 单一模式(向后兼容):使用 push_constant_data/size + // 2. 双阶段模式:使用 vertex_push_constant_*/fragment_push_constant_* + // + // 双阶段模式时,渲染器根据 ShaderSpec 定义的偏移量分别填充顶点和片元数据 + // ======================================================================== - const void* push_constant_data; ///< 用户自定义数据指针(复制到 Custom 区域 [16-127]) - uint32_t push_constant_size; ///< 用户自定义数据大小(字节,最大 112) - bool push_constant_dirty; ///< 用户自定义数据是否需要更新 + /// @brief 用户自定义数据指针(复制到 Custom 区域 [16-127]) + /// @details 单一模式使用。双阶段模式时应为 nullptr + const void* push_constant_data; + + /// @brief 用户自定义数据大小(字节,最大 112) + /// @details 单一模式使用 + uint32_t push_constant_size; + + /// @brief 用户自定义数据是否需要更新 + bool push_constant_dirty; + + // ======================================================================== + // 双阶段 Push Constants(可选,用于顶点/片元独立数据) + // ======================================================================== + + /// @brief 顶点阶段自定义数据指针 + /// @details 当使用双阶段布局时,指向顶点着色器使用的 Custom 数据 + const void* vertex_push_constant_data = nullptr; + + /// @brief 顶点阶段自定义数据大小(字节) + uint32_t vertex_push_constant_size = 0; + + /// @brief 顶点阶段数据在 Custom 区域内的偏移量(相对于 offset 16) + /// @details 由 ShaderSpec 定义,默认为 0 + uint32_t vertex_push_constant_offset = 0; + + /// @brief 片元阶段自定义数据指针 + /// @details 当使用双阶段布局时,指向片元着色器使用的 Custom 数据 + const void* fragment_push_constant_data = nullptr; + + /// @brief 片元阶段自定义数据大小(字节) + uint32_t fragment_push_constant_size = 0; + + /// @brief 片元阶段数据在 Custom 区域内的偏移量(相对于 offset 16) + /// @details 由 ShaderSpec 定义,必须 >= vertex_push_constant_offset + vertex_push_constant_size + uint32_t fragment_push_constant_offset = 0; + + /// @brief 是否使用双阶段 Push Constant 布局 + /// @details 当为 true 时,渲染器使用 vertex/fragment 分离的数据字段 + bool use_dual_stage_push_constants = false; // ======================================================================== // 顶点数据(custom_geometry 模式) @@ -311,6 +355,13 @@ namespace mirage { , push_constant_data(nullptr) , push_constant_size(0) , push_constant_dirty(false) + , vertex_push_constant_data(nullptr) + , vertex_push_constant_size(0) + , vertex_push_constant_offset(0) + , fragment_push_constant_data(nullptr) + , fragment_push_constant_size(0) + , fragment_push_constant_offset(0) + , use_dual_stage_push_constants(false) , vertex_config(std::nullopt) , index_config(std::nullopt) , vertices_dirty(false) @@ -319,6 +370,73 @@ namespace mirage { , instance_data(std::nullopt) { } + // ======================================================================== + // 双阶段 Push Constant 辅助方法 + // ======================================================================== + + /** + * @brief 设置双阶段 Push Constant 数据 + * + * 模板方法用于类型安全地设置顶点和片元阶段的 Push Constant 数据。 + * 自动从类型中提取偏移量信息(如果类型提供 layout_offset 静态成员)。 + * + * @tparam VertexPC 顶点阶段 Push Constant 类型 + * @tparam FragmentPC 片元阶段 Push Constant 类型 + * @param vertex_data 顶点阶段数据指针 + * @param fragment_data 片元阶段数据指针 + */ + template + void set_dual_stage_push_constants(const VertexPC* vertex_data, const FragmentPC* fragment_data) { + static_assert(std::is_standard_layout_v, + "VertexPC must be standard layout type"); + static_assert(std::is_standard_layout_v, + "FragmentPC must be standard layout type"); + + use_dual_stage_push_constants = true; + + if (vertex_data) { + vertex_push_constant_data = vertex_data; + vertex_push_constant_size = static_cast(sizeof(VertexPC)); + // 尝试获取 layout_offset(如果类型提供) + if constexpr (requires { VertexPC::layout_offset; }) { + vertex_push_constant_offset = static_cast(VertexPC::layout_offset); + } + } + + if (fragment_data) { + fragment_push_constant_data = fragment_data; + fragment_push_constant_size = static_cast(sizeof(FragmentPC)); + // 尝试获取 layout_offset(如果类型提供) + if constexpr (requires { FragmentPC::layout_offset; }) { + fragment_push_constant_offset = static_cast(FragmentPC::layout_offset); + } + } + + // 清除单一模式数据 + push_constant_data = nullptr; + push_constant_size = 0; + } + + /** + * @brief 检查是否使用双阶段 Push Constant 布局 + */ + [[nodiscard]] bool is_dual_stage_mode() const noexcept { + return use_dual_stage_push_constants; + } + + /** + * @brief 检查双阶段布局是否有效(无重叠) + * + * 运行时验证,确保片元数据偏移 >= 顶点数据结束位置 + */ + [[nodiscard]] bool validate_dual_stage_layout() const noexcept { + if (!use_dual_stage_push_constants) { + return true; // 非双阶段模式总是有效 + } + // 片元起始偏移必须 >= 顶点结束偏移 + return fragment_push_constant_offset >= (vertex_push_constant_offset + vertex_push_constant_size); + } + // ======================================================================== // 辅助方法 // ======================================================================== diff --git a/src/render/renderers/custom_shader_widget_renderer.cpp b/src/render/renderers/custom_shader_widget_renderer.cpp index be62673..8c222b8 100644 --- a/src/render/renderers/custom_shader_widget_renderer.cpp +++ b/src/render/renderers/custom_shader_widget_renderer.cpp @@ -306,7 +306,7 @@ namespace mirage { } // ============================================================================ - // Push Constants 构建 (简化布局) + // Push Constants 构建 (简化布局 + 双阶段支持) // ============================================================================ void custom_shader_widget_renderer::fill_push_constants( @@ -328,20 +328,109 @@ namespace mirage { // ======================================== // Custom [16-127]: 用户自定义数据 (112 字节) // ======================================== - // 从 command 复制用户的 push_constants_type 数据 - if (command.push_constant_data && command.push_constant_size > 0) { - size_t copy_size = std::min(static_cast(command.push_constant_size), - PUSH_CONSTANT_CUSTOM_MAX_SIZE); - std::memcpy(pc->custom_data, command.push_constant_data, copy_size); + // 先清零整个 Custom 区域,确保未使用部分为零 + std::memset(pc->custom_data, 0, PUSH_CONSTANT_CUSTOM_MAX_SIZE); - // 剩余部分清零 - if (copy_size < PUSH_CONSTANT_CUSTOM_MAX_SIZE) { - std::memset(pc->custom_data + copy_size, 0, PUSH_CONSTANT_CUSTOM_MAX_SIZE - copy_size); - } + // 根据模式选择填充策略 + if (command.use_dual_stage_push_constants) { + // ======================================== + // 双阶段模式:顶点和片元数据分离填充 + // ======================================== + fill_dual_stage_push_constants(command, pc); } else { - // 无用户数据,全部清零 - std::memset(pc->custom_data, 0, PUSH_CONSTANT_CUSTOM_MAX_SIZE); + // ======================================== + // 单一模式(向后兼容) + // ======================================== + // 布局: + // [16-31]: 几何信息 (widget_pos + widget_size) - 16 字节,顶点着色器必需 + // [32-127]: 用户自定义数据 - 96 字节 + fill_legacy_push_constants(command, pc); + } + } + + void custom_shader_widget_renderer::fill_legacy_push_constants( + const custom_shader_widget_command& command, + custom_shader_push_constants* pc) const { + // ======================================== + // 单一模式布局(向后兼容) + // [16-31]: 几何信息 (widget_pos + widget_size) - 16 字节 + // [32-127]: 用户自定义数据 - 96 字节 + // ======================================== + + // 1. 填充几何信息(顶点着色器 custom_shader_quad_procedural.vert.glsl 必需) + float* geo_data = reinterpret_cast(pc->custom_data); + geo_data[0] = command.position.x(); // widget_pos.x at offset 16 + geo_data[1] = command.position.y(); // widget_pos.y at offset 20 + geo_data[2] = command.size.x(); // widget_size.x at offset 24 + geo_data[3] = command.size.y(); // widget_size.y at offset 28 + + // 2. 复制用户数据(从 offset 32 开始,留出 16 字节给几何信息) + constexpr size_t GEO_INFO_SIZE = 16; // widget_pos(8) + widget_size(8) + constexpr size_t USER_DATA_OFFSET = GEO_INFO_SIZE; + constexpr size_t MAX_USER_DATA_SIZE = PUSH_CONSTANT_CUSTOM_MAX_SIZE - GEO_INFO_SIZE; // 96 bytes + + if (command.push_constant_data && command.push_constant_size > 0) { + size_t copy_size = std::min(static_cast(command.push_constant_size), MAX_USER_DATA_SIZE); + std::memcpy(pc->custom_data + USER_DATA_OFFSET, command.push_constant_data, copy_size); + } + } + + void custom_shader_widget_renderer::fill_dual_stage_push_constants( + const custom_shader_widget_command& command, + custom_shader_push_constants* pc) const { + // ======================================== + // 双阶段模式布局 + // 顶点和片元着色器各自使用 Custom 区域的不同部分 + // ======================================== + + // 运行时验证布局有效性 + if (!command.validate_dual_stage_layout()) { + std::cerr << "[CUSTOM_SHADER] 警告: 双阶段 Push Constant 布局无效 - " + << "片元偏移(" << command.fragment_push_constant_offset << ") " + << "< 顶点结束位置(" << (command.vertex_push_constant_offset + command.vertex_push_constant_size) << ")" + << std::endl; + // 降级到单一模式 + fill_legacy_push_constants(command, pc); + return; + } + + // 1. 填充顶点阶段数据 + if (command.vertex_push_constant_data && command.vertex_push_constant_size > 0) { + // 验证偏移量和大小不超出 Custom 区域 + if (command.vertex_push_constant_offset + command.vertex_push_constant_size <= PUSH_CONSTANT_CUSTOM_MAX_SIZE) { + std::memcpy( + pc->custom_data + command.vertex_push_constant_offset, + command.vertex_push_constant_data, + command.vertex_push_constant_size + ); + } + else { + std::cerr << "[CUSTOM_SHADER] 错误: 顶点 Push Constant 超出边界 (offset=" + << command.vertex_push_constant_offset + << ", size=" << command.vertex_push_constant_size + << ", max=" << PUSH_CONSTANT_CUSTOM_MAX_SIZE << ")" + << std::endl; + } + } + + // 2. 填充片元阶段数据 + if (command.fragment_push_constant_data && command.fragment_push_constant_size > 0) { + // 验证偏移量和大小不超出 Custom 区域 + if (command.fragment_push_constant_offset + command.fragment_push_constant_size <= PUSH_CONSTANT_CUSTOM_MAX_SIZE) { + std::memcpy( + pc->custom_data + command.fragment_push_constant_offset, + command.fragment_push_constant_data, + command.fragment_push_constant_size + ); + } + else { + std::cerr << "[CUSTOM_SHADER] 错误: 片元 Push Constant 超出边界 (offset=" + << command.fragment_push_constant_offset + << ", size=" << command.fragment_push_constant_size + << ", max=" << PUSH_CONSTANT_CUSTOM_MAX_SIZE << ")" + << std::endl; + } } } diff --git a/src/render/renderers/custom_shader_widget_renderer.h b/src/render/renderers/custom_shader_widget_renderer.h index 5a863c7..8376760 100644 --- a/src/render/renderers/custom_shader_widget_renderer.h +++ b/src/render/renderers/custom_shader_widget_renderer.h @@ -332,19 +332,35 @@ namespace mirage { vk::DescriptorPool descriptor_pool); // ======================================== - // Push Constant 填充 (简化布局) + // Push Constant 填充 (简化布局 + 双阶段支持) // ======================================== /// @brief 填充 Push Constants 到缓冲区 /// @param command 渲染命令 /// @param buffer 目标缓冲区 (128 字节) - /// @details 新布局: - /// - [0-15]: Header (uScale + uTranslate) - /// - [16-127]: 用户自定义数据 (从 command 复制) + /// @details 支持两种模式: + /// - 单一模式(向后兼容): [0-15] Header, [16-31] 几何信息, [32-127] 用户数据 + /// - 双阶段模式: [0-15] Header, [16-127] 顶点+片元分离的用户数据 void fill_push_constants( const custom_shader_widget_command& command, void* buffer) const; + /// @brief 填充单一模式 Push Constants(向后兼容) + /// @param command 渲染命令 + /// @param pc 目标结构体 + /// @details 布局: [16-31] 几何信息, [32-127] 用户数据 + void fill_legacy_push_constants( + const custom_shader_widget_command& command, + custom_shader_push_constants* pc) const; + + /// @brief 填充双阶段模式 Push Constants + /// @param command 渲染命令 + /// @param pc 目标结构体 + /// @details 顶点和片元数据分别填充到指定偏移位置 + void fill_dual_stage_push_constants( + const custom_shader_widget_command& command, + custom_shader_push_constants* pc) const; + // ======================================== // Pipeline 管理 // ======================================== diff --git a/src/render/renderers/geometry_renderer.cpp b/src/render/renderers/geometry_renderer.cpp index 2b82b07..a064100 100644 --- a/src/render/renderers/geometry_renderer.cpp +++ b/src/render/renderers/geometry_renderer.cpp @@ -3,7 +3,6 @@ #include "render/vulkan/pipeline/graphics_pipeline.h" #include "render/uniform_types.h" #include -#include // 引入着色器 SPIR-V 数据 #include "rect_vert_frag.h" @@ -46,10 +45,11 @@ namespace mirage { auto frag_shader = frag_shader_result.value(); // 定义 Push Constants 范围 + // 使用 push_constants_header_t,只包含 NDC 变换参数 (scale + translate) vk::PushConstantRange push_constant_range{ vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0, - sizeof(push_constants) + sizeof(push_constants_header_t) }; // 创建 Pipeline Layout @@ -330,17 +330,14 @@ namespace mirage { cmd.setScissor(0, 1, &scissor); // 更新 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); + // 使用 push_constants_header_t,只包含 NDC 变换参数 + auto constants = create_push_constants_header(viewport_size_.x(), viewport_size_.y()); cmd.pushConstants( layout_.get(), vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0, - sizeof(push_constants), + sizeof(push_constants_header_t), &constants ); diff --git a/src/render/renderers/text_renderer.cpp b/src/render/renderers/text_renderer.cpp index 8e1e0c3..d9168da 100644 --- a/src/render/renderers/text_renderer.cpp +++ b/src/render/renderers/text_renderer.cpp @@ -111,11 +111,11 @@ namespace mirage { auto frag_shader = frag_shader_result.value(); // 定义Push Constants范围 - // 注意:由于添加了screen_resolution参数,push_constants结构体大小已改变 + // 使用 text_push_constants_t,包含 NDC 变换参数和屏幕分辨率 vk::PushConstantRange push_constant_range{ vk::ShaderStageFlagBits::eVertex, 0, - sizeof(push_constants) + sizeof(text_push_constants_t) }; // 创建Pipeline Layout @@ -430,14 +430,13 @@ namespace mirage { ); // 更新Push Constants - // 注意:这里使用viewport_size作为屏幕分辨率,因为在UI渲染中它们通常是相同的 - // 如果需要支持不同的屏幕分辨率(如DPI缩放),可以在这里传入实际的屏幕分辨率 - auto constants = create_push_constants(viewport_size_.x(), viewport_size_.y(), 0.0f); + // 使用 text_push_constants_t,包含 NDC 变换参数和屏幕分辨率 + auto constants = create_text_push_constants(viewport_size_.x(), viewport_size_.y()); cmd.pushConstants( pipeline_layout_.get(), vk::ShaderStageFlagBits::eVertex, 0, - sizeof(push_constants), + sizeof(text_push_constants_t), &constants ); diff --git a/src/render/uniform_types.h b/src/render/uniform_types.h index 8c4b64c..90e14f3 100644 --- a/src/render/uniform_types.h +++ b/src/render/uniform_types.h @@ -8,147 +8,7 @@ namespace mirage { - // ============================================================================ - // 基础渲染器 Push Constants(用于 geometry, image, text, mask) - // ============================================================================ - - /** - * @brief Push Constants 基础部分 (80 字节) - * - * 用于基础渲染器(geometry, image, text, mask)的 push constants。 - * 包含投影矩阵、视口大小和时间参数。 - * - * 对应 GLSL 布局: - * @code - * 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_base { - float projection[16]; ///< 正交投影矩阵(列主序,符合 GLSL mat4 布局) - float viewport_size[2]; ///< 视口大小 (width, height) - float time; ///< 时间(秒),用于动画效果 - float _pad0; ///< 对齐填充 - }; - - static_assert(sizeof(push_constants_base) == 80, - "push_constants_base must be exactly 80 bytes"); - static_assert(offsetof(push_constants_base, projection) == 0, - "push_constants_base::projection must be at offset 0"); - static_assert(offsetof(push_constants_base, viewport_size) == 64, - "push_constants_base::viewport_size must be at offset 64"); - static_assert(offsetof(push_constants_base, time) == 72, - "push_constants_base::time must be at offset 72"); - - /** - * @brief 创建正交投影矩阵 - * - * 创建用于 2D UI 渲染的正交投影矩阵。 - * 坐标系: 左上角为原点 (0,0),x 向右增加,y 向下增加。 - * - * @param width 视口宽度(像素) - * @param height 视口高度(像素) - * @return 列主序的 4x4 正交投影矩阵 - */ - inline auto create_ortho_projection(float width, float height) -> std::array { - // 正交投影矩阵(列主序) - // 将屏幕空间 [0, width] x [0, height] 映射到 NDC [-1, 1] x [-1, 1] - // 注意: Vulkan NDC 的 Y 轴向下,与屏幕坐标一致 - return { - { - 2.0f / width, 0.0f, 0.0f, 0.0f, // 第一列 - 0.0f, 2.0f / height, 0.0f, 0.0f, // 第二列 - 0.0f, 0.0f, 1.0f, 0.0f, // 第三列 - -1.0f, -1.0f, 0.0f, 1.0f // 第四列(平移) - } - }; - } - - /** - * @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 结构 (96 字节) - * - * 用于基础渲染器的扩展参数,包含屏幕分辨率信息。 - * - * 对应 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 创建 Push Constants - * - * @param width 视口宽度 - * @param height 视口高度 - * @param time 当前时间(秒) - * @return 初始化的 push_constants 结构 - */ - inline auto create_push_constants(float width, float height, float time = 0.0f) -> push_constants { - push_constants constants{}; - auto proj = create_ortho_projection(width, height); - 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.screen_resolution[1] = height; - constants.time = time; - constants.padding[0] = 0.0f; - constants.padding[1] = 0.0f; - constants.padding[2] = 0.0f; - return constants; - } - - // ============================================================================ - // Custom Shader Widget Push Constants - 新简化布局 + // Custom Shader Widget Push Constants - 简化布局 // ============================================================================ // // 新布局 (128 bytes): @@ -204,7 +64,61 @@ namespace mirage { } /** - * @brief Custom Shader Widget Push Constants 完整布局 (128 字节) + * @brief Text Renderer Push Constants (24 字节) + * + * 用于文本渲染的 Push Constants,包含: + * - scale: NDC 缩放因子 (8 字节) + * - translate: NDC 平移因子 (8 字节) + * - screen_resolution: 屏幕分辨率 (8 字节) + * + * 注意:不能直接嵌入 push_constants_header_t,因为它有 alignas(16) 对齐, + * 会导致结构体被填充到 32 字节。 + * + * 对应 GLSL 布局: + * @code + * layout(push_constant) uniform PushConstants { + * vec2 scale; // 偏移 0, 大小 8 + * vec2 translate; // 偏移 8, 大小 8 + * vec2 screen_resolution; // 偏移 16, 大小 8 + * } pc; + * @endcode + */ + struct text_push_constants_t { + float scale[2]; ///< 缩放因子: (2.0f / width, 2.0f / height) + float translate[2]; ///< 平移偏移: (-1.0f, -1.0f) + float screen_resolution[2]; ///< 屏幕分辨率: (width, height) + }; + + static_assert(sizeof(text_push_constants_t) == 24, + "text_push_constants_t must be exactly 24 bytes"); + static_assert(offsetof(text_push_constants_t, scale) == 0, + "text_push_constants_t::scale must be at offset 0"); + static_assert(offsetof(text_push_constants_t, translate) == 8, + "text_push_constants_t::translate must be at offset 8"); + static_assert(offsetof(text_push_constants_t, screen_resolution) == 16, + "text_push_constants_t::screen_resolution must be at offset 16"); + + /** + * @brief 创建 Text Push Constants + * + * @param width 视口宽度(像素) + * @param height 视口高度(像素) + * @return 初始化的 text_push_constants_t 结构 + */ + inline auto create_text_push_constants(float width, float height) + -> text_push_constants_t { + text_push_constants_t pc{}; + pc.scale[0] = 2.0f / width; + pc.scale[1] = 2.0f / height; + pc.translate[0] = -1.0f; + pc.translate[1] = -1.0f; + pc.screen_resolution[0] = width; + pc.screen_resolution[1] = height; + return pc; + } + + /** + * @brief Custom Shader Widget Push Constants 完整布局 (128 字节) * * 简化布局设计: * - Header [0-15]: uScale + uTranslate (16 字节) - 固定用途 diff --git a/tools/code_generator.py b/tools/code_generator.py index d10591a..3bb0f2f 100644 --- a/tools/code_generator.py +++ b/tools/code_generator.py @@ -20,17 +20,20 @@ from .type_mapping import ( from .types import ( ArrayTypeInfo, BufferInfo, + CombinedPushConstantInfo, CompilationResult, InstanceBufferInfo, MemberInfo, PushConstantInfo, ShaderMetadata, SPIRVReflection, + StagePushConstantInfo, ToolError, TypeInfo, VertexAttribute, VertexLayout, ) +from .spirv_parser import analyze_dual_stage_push_constants from .constants import ( PUSH_CONSTANT_HEADER_SIZE, PUSH_CONSTANT_CUSTOM_OFFSET, @@ -38,6 +41,41 @@ from .constants import ( ) +def check_supports_procedural_vertex_shader(reflection: SPIRVReflection) -> bool: + """检测片元着色器是否支持 custom_shader_quad_procedural 顶点着色器 + + 检测条件:片元着色器的 push_constant 中第一个用户自定义成员的偏移正好是 16 字节。 + + 这意味着片元着色器的 push_constant 布局: + - Header [0-15]: scale + translate (由顶点着色器定义并填充) + - Custom [16-127]: 用户自定义数据 (第一个成员从 offset 16 开始) + + Args: + reflection: SPIR-V 反射信息(通常来自片元着色器) + + Returns: + True 如果支持 procedural 顶点着色器,否则 False + """ + push_constant = reflection.push_constant + if not push_constant: + return False + + # 检查原始结构体成员,找到 offset >= 16 的第一个成员 + struct_type = push_constant.struct_type + if not struct_type or not struct_type.members: + return False + + # 找到所有 offset >= PUSH_CONSTANT_CUSTOM_OFFSET 的成员 + custom_members = [m for m in struct_type.members if m.offset >= PUSH_CONSTANT_CUSTOM_OFFSET] + + if not custom_members: + return False + + # 检查第一个 custom 成员的偏移是否正好是 16 + first_custom_offset = min(m.offset for m in custom_members) + return first_custom_offset == PUSH_CONSTANT_CUSTOM_OFFSET + + # ============ Structure Generation ============ def generate_buffer_structures(reflection: SPIRVReflection) -> str: @@ -269,6 +307,287 @@ def generate_push_constant_base_layout(reflection: SPIRVReflection, shader_name: return generate_push_constant_header_layout(reflection, shader_name) +# ============ Dual Stage Push Constants Structure Generation ============ + +def _generate_stage_push_constant_struct( + stage_info: StagePushConstantInfo, + shader_name: str, + type_map: Dict[int, TypeInfo] +) -> str: + """生成单个阶段的 Push Constant 结构体 + + Args: + stage_info: 阶段 Push Constant 信息 + shader_name: 着色器名称 + type_map: 类型映射 + + Returns: + 生成的 C++ 结构体代码 + """ + if not stage_info or not stage_info.has_members: + return "" + + # 生成结构体名称 + stage_suffix = "Vertex" if stage_info.stage == "vertex" else "Fragment" + struct_name = f"{_to_pascal_case(shader_name)}{stage_suffix}PushConstant" + + # 准备成员信息 + members = [] + for member in stage_info.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': stage_info.relative_offset, + 'members': members, + } + + return renderer.render('push_constant/custom_struct.jinja2', context) + + +def _generate_dual_stage_fill_function( + combined_info: CombinedPushConstantInfo, + shader_name: str, + type_map: Dict[int, TypeInfo] +) -> str: + """生成双阶段 Push Constant 填充函数 + + 根据布局模式生成不同的填充函数: + - 共享模式:单一填充函数,同时填充两个阶段 + - 分离模式:分别为每个阶段生成填充函数 + + Args: + combined_info: 合并后的双阶段信息 + shader_name: 着色器名称 + type_map: 类型映射 + + Returns: + 生成的 C++ 填充函数代码 + """ + lines = [] + pascal_name = _to_pascal_case(shader_name) + + if combined_info.is_shared_layout: + # 共享模式:生成单一填充函数 + struct_name = f"{pascal_name}PushConstantCustom" + lines.append(f"/**") + lines.append(f" * @brief 填充 Push Constants Custom 区域(共享布局)") + lines.append(f" * ") + lines.append(f" * 顶点和片元着色器共享相同的 Custom 区域布局。") + lines.append(f" * ") + lines.append(f" * @param cmd 命令缓冲") + lines.append(f" * @param layout 管线布局") + lines.append(f" * @param data Custom 区域数据") + lines.append(f" */") + lines.append(f"inline void fill_push_constants(") + lines.append(f" vk::CommandBuffer cmd,") + lines.append(f" vk::PipelineLayout layout,") + lines.append(f" const {struct_name}& data") + lines.append(f") {{") + lines.append(f" cmd.pushConstants(") + lines.append(f" layout,") + lines.append(f" vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,") + lines.append(f" PUSH_CONSTANT_CUSTOM_OFFSET,") + lines.append(f" sizeof({struct_name}),") + lines.append(f" &data") + lines.append(f" );") + lines.append(f"}}") + else: + # 分离模式:为每个阶段生成填充函数 + if combined_info.has_vertex: + vert_struct = f"{pascal_name}VertexPushConstant" + lines.append(f"/**") + lines.append(f" * @brief 填充顶点着色器 Push Constants Custom 区域") + lines.append(f" * ") + lines.append(f" * @param cmd 命令缓冲") + lines.append(f" * @param layout 管线布局") + lines.append(f" * @param data 顶点着色器 Custom 区域数据") + lines.append(f" */") + lines.append(f"inline void fill_vertex_push_constants(") + lines.append(f" vk::CommandBuffer cmd,") + lines.append(f" vk::PipelineLayout layout,") + lines.append(f" const {vert_struct}& data") + lines.append(f") {{") + lines.append(f" cmd.pushConstants(") + lines.append(f" layout,") + lines.append(f" vk::ShaderStageFlagBits::eVertex,") + lines.append(f" PUSH_CONSTANT_CUSTOM_OFFSET,") + lines.append(f" sizeof({vert_struct}),") + lines.append(f" &data") + lines.append(f" );") + lines.append(f"}}") + lines.append(f"") + + if combined_info.has_fragment: + frag_struct = f"{pascal_name}FragmentPushConstant" + lines.append(f"/**") + lines.append(f" * @brief 填充片元着色器 Push Constants Custom 区域") + lines.append(f" * ") + lines.append(f" * @param cmd 命令缓冲") + lines.append(f" * @param layout 管线布局") + lines.append(f" * @param data 片元着色器 Custom 区域数据") + lines.append(f" */") + lines.append(f"inline void fill_fragment_push_constants(") + lines.append(f" vk::CommandBuffer cmd,") + lines.append(f" vk::PipelineLayout layout,") + lines.append(f" const {frag_struct}& data") + lines.append(f") {{") + lines.append(f" cmd.pushConstants(") + lines.append(f" layout,") + lines.append(f" vk::ShaderStageFlagBits::eFragment,") + lines.append(f" PUSH_CONSTANT_CUSTOM_OFFSET,") + lines.append(f" sizeof({frag_struct}),") + lines.append(f" &data") + lines.append(f" );") + lines.append(f"}}") + + return "\n".join(lines) + + +def _generate_dual_stage_layout_constants( + combined_info: CombinedPushConstantInfo, + shader_name: str +) -> str: + """生成双阶段 Push Constant 布局常量 + + Args: + combined_info: 合并后的双阶段信息 + shader_name: 着色器名称 + + Returns: + 生成的 C++ 布局常量代码 + """ + lines = [] + pascal_name = _to_pascal_case(shader_name) + + lines.append(f"/**") + lines.append(f" * @brief {pascal_name} Push Constant 布局信息") + lines.append(f" */") + lines.append(f"struct {pascal_name}PushConstantLayout {{") + lines.append(f" /// 是否使用共享布局模式") + lines.append(f" static constexpr bool is_shared_layout = {'true' if combined_info.is_shared_layout else 'false'};") + lines.append(f" ") + + if combined_info.has_vertex: + lines.append(f" /// 顶点着色器 Custom 区域大小") + lines.append(f" static constexpr std::uint32_t vertex_custom_size = {combined_info.vertex_info.total_size};") + + if combined_info.has_fragment: + lines.append(f" /// 片元着色器 Custom 区域大小") + lines.append(f" static constexpr std::uint32_t fragment_custom_size = {combined_info.fragment_info.total_size};") + + if combined_info.shared_members: + lines.append(f" ") + lines.append(f" /// 共享的成员数量") + lines.append(f" static constexpr std::size_t shared_member_count = {len(combined_info.shared_members)};") + + lines.append(f"}};") + + return "\n".join(lines) + + +def generate_dual_stage_push_constant_structs( + vert_reflection: SPIRVReflection, + frag_reflection: SPIRVReflection, + shader_name: str +) -> str: + """生成双阶段 Push Constant 结构体和填充函数 + + 分析顶点和片元着色器的 Push Constant 布局,生成: + - {ShaderName}VertexPushConstant 结构体(如果有顶点专用数据) + - {ShaderName}FragmentPushConstant 结构体(如果有片元专用数据) + - {ShaderName}PushConstantCustom 结构体(共享模式时) + - fill_push_constants() 或 fill_vertex/fragment_push_constants() 填充函数 + - {ShaderName}PushConstantLayout 布局信息结构体 + + Args: + vert_reflection: 顶点着色器的 SPIR-V 反射信息 + frag_reflection: 片元着色器的 SPIR-V 反射信息 + shader_name: 着色器名称 + + Returns: + 生成的 C++ 代码,如果没有 Custom 数据则返回空字符串 + + Example: + >>> vert_ref = parse_spirv_type_system(vert_spirv, "vertex") + >>> frag_ref = parse_spirv_type_system(frag_spirv, "fragment") + >>> code = generate_dual_stage_push_constant_structs(vert_ref, frag_ref, "my_shader") + >>> print(code) + // 生成的结构体定义和填充函数... + """ + # 分析双阶段布局 + combined_info = analyze_dual_stage_push_constants(vert_reflection, frag_reflection) + + # 如果两个阶段都没有 Custom 成员,返回空字符串 + if not combined_info.has_vertex and not combined_info.has_fragment: + return "" + + sections = [] + pascal_name = _to_pascal_case(shader_name) + + # 添加注释头 + sections.append(f"// ============ {pascal_name} Push Constants (Dual Stage) ============") + sections.append("") + + if combined_info.is_shared_layout: + # 共享模式:使用现有的 generate_push_constant_custom_struct + # 优先使用片元着色器的反射信息(通常更完整) + ref = frag_reflection if frag_reflection.push_constant else vert_reflection + custom_struct = generate_push_constant_custom_struct(ref, shader_name) + if custom_struct: + sections.append(custom_struct) + sections.append("") + else: + # 分离模式:为每个阶段生成独立结构体 + type_map = vert_reflection.types if vert_reflection else frag_reflection.types + + if combined_info.has_vertex: + vert_struct = _generate_stage_push_constant_struct( + combined_info.vertex_info, + shader_name, + type_map + ) + if vert_struct: + sections.append(vert_struct) + sections.append("") + + if combined_info.has_fragment: + frag_struct = _generate_stage_push_constant_struct( + combined_info.fragment_info, + shader_name, + frag_reflection.types if frag_reflection else type_map + ) + if frag_struct: + sections.append(frag_struct) + sections.append("") + + # 生成填充函数 + type_map = vert_reflection.types if vert_reflection else frag_reflection.types + fill_function = _generate_dual_stage_fill_function(combined_info, shader_name, type_map) + if fill_function: + sections.append(fill_function) + sections.append("") + + # 生成布局常量 + layout_constants = _generate_dual_stage_layout_constants(combined_info, shader_name) + if layout_constants: + sections.append(layout_constants) + sections.append("") + + return "\n".join(sections) + + # ============ Instance Data Structure Generation ============ def generate_instance_data_struct(instance_buffer: InstanceBufferInfo, type_map: Dict[int, TypeInfo]) -> str: @@ -777,6 +1096,12 @@ def generate_header( # 准备顶点布局相关变量 vertex_info = generate_vertex_layout_info(metadata.reflection, metadata.name) + # 检测是否支持 procedural 顶点着色器 + # 条件:片元着色器的 push_constant 第一个用户自定义成员偏移为 16 字节 + supports_procedural_vertex_shader = False + if metadata.reflection: + supports_procedural_vertex_shader = check_supports_procedural_vertex_shader(metadata.reflection) + # 使用模板渲染 renderer = get_renderer() context = { @@ -813,6 +1138,8 @@ def generate_header( 'push_constant_base_member_offsets': push_constant_base_member_offsets, # Push Constants 静态检查代码 'push_constant_static_check': push_constant_static_check, + # 是否支持 procedural 顶点着色器 + 'supports_procedural_vertex_shader': supports_procedural_vertex_shader, # 实例化渲染相关变量 **instance_info, # 顶点布局相关变量 diff --git a/tools/spirv_parser.py b/tools/spirv_parser.py index 49f83e8..344f5f2 100644 --- a/tools/spirv_parser.py +++ b/tools/spirv_parser.py @@ -54,12 +54,13 @@ from .constants import ( STORAGE_CLASS_UNIFORM, STORAGE_CLASS_UNIFORM_CONSTANT, ) -from .type_mapping import calculate_std430_size, spirv_type_to_cpp, spirv_type_to_compact_cpp +from .type_mapping import calculate_std430_alignment, calculate_std430_size, spirv_type_to_cpp, spirv_type_to_compact_cpp from .types import ( ArrayTypeInfo, BaseType, BindingInfo, BufferInfo, + CombinedPushConstantInfo, InstanceBufferInfo, MatrixTypeInfo, MemberInfo, @@ -70,6 +71,7 @@ from .types import ( PushConstantInfo, ScalarTypeInfo, SPIRVReflection, + StagePushConstantInfo, StructTypeInfo, ToolError, TypeInfo, @@ -1146,4 +1148,191 @@ def extract_spirv_reflection( 'entry_point': reflection.entry_point, 'bindings': bindings, 'reflection': reflection, - } \ No newline at end of file + } + + +# ============ Dual Stage Push Constants Analysis ============ + +def _extract_stage_push_constant_info( + reflection: SPIRVReflection, + stage: str +) -> Optional[StagePushConstantInfo]: + """从反射信息中提取单个阶段的 Push Constant 信息 + + 提取 Custom 区域(偏移 >= 16)的成员信息。 + + Args: + reflection: SPIR-V 反射信息 + stage: 着色器阶段名称("vertex" 或 "fragment") + + Returns: + StagePushConstantInfo 或 None + """ + push_constant = reflection.push_constant + if not push_constant or not push_constant.effect_members: + return None + + # effect_members 已经是 Custom 区域成员(偏移量已调整为相对偏移) + members = push_constant.effect_members + + if not members: + return None + + # 计算总大小和对齐 + total_size = 0 + max_alignment = 4 + + for member in members: + member_size = calculate_std430_size(member.resolved_type, reflection.types) + member_alignment = calculate_std430_alignment(member.resolved_type, reflection.types) + + # 更新结束位置 + end_offset = member.offset + member_size + if end_offset > total_size: + total_size = end_offset + + # 更新最大对齐 + if member_alignment > max_alignment: + max_alignment = member_alignment + + return StagePushConstantInfo( + stage=stage, + members=members, + relative_offset=PUSH_CONSTANT_CUSTOM_OFFSET, + total_size=total_size, + alignment=max_alignment, + ) + + +def _compare_member_layouts( + vert_members: List[MemberInfo], + frag_members: List[MemberInfo], + type_map: Dict[int, TypeInfo] +) -> Tuple[bool, List[str]]: + """比较两个阶段的成员布局 + + 检查成员是否具有相同的名称、偏移量和类型。 + + Args: + vert_members: 顶点着色器成员列表 + frag_members: 片元着色器成员列表 + type_map: 类型映射 + + Returns: + (is_identical, shared_member_names) 元组: + - is_identical: 布局是否完全相同 + - shared_member_names: 共享的成员名称列表 + """ + if not vert_members and not frag_members: + return True, [] + + if not vert_members or not frag_members: + return False, [] + + # 构建成员映射(按名称) + vert_by_name = {m.name: m for m in vert_members} + frag_by_name = {m.name: m for m in frag_members} + + # 找到共享的成员名称 + shared_names = set(vert_by_name.keys()) & set(frag_by_name.keys()) + + if not shared_names: + return False, [] + + # 检查共享成员的布局是否相同 + shared_members = [] + for name in shared_names: + vert_member = vert_by_name[name] + frag_member = frag_by_name[name] + + # 检查偏移量是否相同 + if vert_member.offset != frag_member.offset: + continue + + # 检查类型大小是否相同 + vert_size = calculate_std430_size(vert_member.resolved_type, type_map) + frag_size = calculate_std430_size(frag_member.resolved_type, type_map) + if vert_size != frag_size: + continue + + shared_members.append(name) + + # 判断是否完全相同(所有成员都匹配且数量相同) + is_identical = ( + len(shared_members) == len(vert_members) == len(frag_members) and + len(shared_members) > 0 + ) + + return is_identical, sorted(shared_members) + + +def analyze_dual_stage_push_constants( + vert_reflection: SPIRVReflection, + frag_reflection: SPIRVReflection +) -> CombinedPushConstantInfo: + """分析顶点和片元着色器的 Push Constant 布局 + + 分析两个着色器阶段的 Push Constant Custom 区域布局: + 1. 提取各阶段的 Custom 成员(偏移量 >= 16 的成员) + 2. 检测布局是否相同(共享模式)或不同(分离模式) + 3. 返回组合信息 + + 布局分析规则: + - 如果两个阶段的 Custom 区域成员完全相同(名称、偏移、类型大小都匹配), + 则使用共享模式(overlapping=True) + - 如果不同,则使用分离模式(overlapping=False) + + Args: + vert_reflection: 顶点着色器的 SPIR-V 反射信息 + frag_reflection: 片元着色器的 SPIR-V 反射信息 + + Returns: + CombinedPushConstantInfo 包含双阶段分析结果 + + Example: + >>> vert_ref = parse_spirv_type_system(vert_spirv, "vertex") + >>> frag_ref = parse_spirv_type_system(frag_spirv, "fragment") + >>> combined = analyze_dual_stage_push_constants(vert_ref, frag_ref) + >>> if combined.is_shared_layout: + >>> print("使用共享 Push Constant 布局") + >>> else: + >>> print("使用分离 Push Constant 布局") + """ + # 提取各阶段的 Push Constant 信息 + vert_info = _extract_stage_push_constant_info(vert_reflection, "vertex") + frag_info = _extract_stage_push_constant_info(frag_reflection, "fragment") + + # 如果两个阶段都没有 Custom 成员,返回空结果 + if not vert_info and not frag_info: + return CombinedPushConstantInfo( + vertex_info=None, + fragment_info=None, + overlapping=False, + shared_members=[], + ) + + # 如果只有一个阶段有 Custom 成员,不存在重叠 + if not vert_info or not frag_info: + return CombinedPushConstantInfo( + vertex_info=vert_info, + fragment_info=frag_info, + overlapping=False, + shared_members=[], + ) + + # 使用顶点着色器的类型映射(两者应该是兼容的) + type_map = vert_reflection.types + + # 比较两个阶段的成员布局 + is_identical, shared_members = _compare_member_layouts( + vert_info.members, + frag_info.members, + type_map, + ) + + return CombinedPushConstantInfo( + vertex_info=vert_info, + fragment_info=frag_info, + overlapping=is_identical, + shared_members=shared_members, + ) \ No newline at end of file diff --git a/tools/templates/base/header.jinja2 b/tools/templates/base/header.jinja2 index 7247638..de3bfd7 100644 --- a/tools/templates/base/header.jinja2 +++ b/tools/templates/base/header.jinja2 @@ -222,6 +222,11 @@ struct {{ shader_spec_name }} { using push_constant_base_layout = {{ shader_name }}_push_constant_header_layout; {%- endif %} + // ========== Procedural 顶点着色器兼容性 ========== + /// @brief 是否支持 custom_shader_quad_procedural 顶点着色器 + /// @note 检测条件:push_constant 第一个用户自定义成员偏移为 16 字节 + static constexpr bool supports_procedural_vertex_shader = {{ 'true' if supports_procedural_vertex_shader else 'false' }}; + // ========== 实例化渲染支持 ========== {%- if has_instance_data %} /// @brief 实例数据类型 diff --git a/tools/templates/push_constant/dual_stage.jinja2 b/tools/templates/push_constant/dual_stage.jinja2 new file mode 100644 index 0000000..9bb6328 --- /dev/null +++ b/tools/templates/push_constant/dual_stage.jinja2 @@ -0,0 +1,199 @@ +{# 生成双阶段 Push Constants 结构体和填充函数 #} +{# 用于支持顶点着色器和片元着色器使用不同 Custom 区域的场景 #} +{# + 模板上下文变量: + - shader_name: 着色器名称(Pascal 大小写) + - combined_info: CombinedPushConstantInfo 对象 + - vertex_struct_name: 顶点结构体名称 (如 "MyShaderVertexPushConstant") + - fragment_struct_name: 片元结构体名称 (如 "MyShaderFragmentPushConstant") + - vertex_members: 顶点着色器 Custom 成员列表 + - fragment_members: 片元着色器 Custom 成员列表 + - is_shared_layout: 是否为共享布局模式 + - shared_struct_name: 共享结构体名称 (如 "MyShaderPushConstantCustom") +#} + +// ============ {{ shader_name }} Push Constants (Dual Stage) ============ +// 双阶段 Push Constants 布局:支持顶点/片元着色器独立的 Custom 区域 +// Header [0-15]: scale + translate (16 字节) - 由系统填充 +// Custom [16-127]: 用户自定义数据 (112 字节) + +{%- if is_shared_layout %} + +// ========== 共享布局模式 ========== +// 顶点和片元着色器共享相同的 Custom 区域布局 + +/// {{ shader_name }} Push Constants Custom 区域 (共享) +/// 偏移量: 16-{{ 16 + shared_total_size - 1 if shared_total_size > 0 else 127 }} +struct alignas({{ shared_alignment }}) {{ shared_struct_name }} { + /// 在 Push Constant 布局中的绝对偏移量 + static constexpr std::size_t layout_offset = mirage::PUSH_CONSTANT_CUSTOM_OFFSET; + +{%- for member in shared_members %} + /// 偏移量: {{ member.offset }}, 大小: {{ member.size }}, 对齐: {{ member.alignment }} + alignas({{ member.alignment }}) {{ member.cpp_type }} {{ member.name }}; +{%- endfor %} +}; + +static_assert( + sizeof({{ shared_struct_name }}) <= mirage::PUSH_CONSTANT_CUSTOM_MAX_SIZE, + "{{ shared_struct_name }}: custom data must not exceed 112 bytes" +); + +/** + * @brief 填充 Push Constants Custom 区域(共享布局) + * + * 顶点和片元着色器共享相同的 Custom 区域布局。 + * + * @param cmd 命令缓冲 + * @param layout 管线布局 + * @param data Custom 区域数据 + */ +inline void fill_push_constants( + vk::CommandBuffer cmd, + vk::PipelineLayout layout, + const {{ shared_struct_name }}& data +) { + cmd.pushConstants( + layout, + vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, + mirage::PUSH_CONSTANT_CUSTOM_OFFSET, + sizeof({{ shared_struct_name }}), + &data + ); +} + +{%- else %} + +// ========== 分离布局模式 ========== +// 顶点和片元着色器使用不同的 Custom 区域布局 + +{%- if has_vertex and vertex_members %} + +/// {{ shader_name }} 顶点阶段 Push Constants [offset {{ vertex_relative_offset }}-{{ vertex_relative_offset + vertex_total_size - 1 if vertex_total_size > 0 else vertex_relative_offset }}] +struct alignas({{ vertex_alignment }}) {{ vertex_struct_name }} { + /// 在 Push Constant 布局中的绝对偏移量 + static constexpr std::size_t layout_offset = mirage::PUSH_CONSTANT_CUSTOM_OFFSET; + +{%- for member in vertex_members %} + /// 偏移量: {{ member.offset }}, 大小: {{ member.size }}, 对齐: {{ member.alignment }} + alignas({{ member.alignment }}) {{ member.cpp_type }} {{ member.name }}; +{%- endfor %} +}; + +static_assert( + sizeof({{ vertex_struct_name }}) <= mirage::PUSH_CONSTANT_CUSTOM_MAX_SIZE, + "{{ vertex_struct_name }}: custom data must not exceed 112 bytes" +); + +{%- endif %} + +{%- if has_fragment and fragment_members %} + +/// {{ shader_name }} 片元阶段 Push Constants [offset {{ fragment_relative_offset }}-{{ fragment_relative_offset + fragment_total_size - 1 if fragment_total_size > 0 else fragment_relative_offset }}] +struct alignas({{ fragment_alignment }}) {{ fragment_struct_name }} { + /// 在 Push Constant 布局中的绝对偏移量 (Header + 顶点数据大小) + static constexpr std::size_t layout_offset = mirage::PUSH_CONSTANT_CUSTOM_OFFSET + {{ vertex_total_size }}; + +{%- for member in fragment_members %} + /// 偏移量: {{ member.offset }}, 大小: {{ member.size }}, 对齐: {{ member.alignment }} + alignas({{ member.alignment }}) {{ member.cpp_type }} {{ member.name }}; +{%- endfor %} +}; + +static_assert( + sizeof({{ fragment_struct_name }}) <= mirage::PUSH_CONSTANT_CUSTOM_MAX_SIZE, + "{{ fragment_struct_name }}: custom data must not exceed 112 bytes" +); + +{%- endif %} + +{%- if has_vertex and vertex_members %} + +/** + * @brief 填充顶点着色器 Push Constants Custom 区域 + * + * @param cmd 命令缓冲 + * @param layout 管线布局 + * @param data 顶点着色器 Custom 区域数据 + */ +inline void fill_vertex_push_constants( + vk::CommandBuffer cmd, + vk::PipelineLayout layout, + const {{ vertex_struct_name }}& data +) { + cmd.pushConstants( + layout, + vk::ShaderStageFlagBits::eVertex, + mirage::PUSH_CONSTANT_CUSTOM_OFFSET, + sizeof({{ vertex_struct_name }}), + &data + ); +} + +{%- endif %} + +{%- if has_fragment and fragment_members %} + +/** + * @brief 填充片元着色器 Push Constants Custom 区域 + * + * @param cmd 命令缓冲 + * @param layout 管线布局 + * @param data 片元着色器 Custom 区域数据 + */ +inline void fill_fragment_push_constants( + vk::CommandBuffer cmd, + vk::PipelineLayout layout, + const {{ fragment_struct_name }}& data +) { + cmd.pushConstants( + layout, + vk::ShaderStageFlagBits::eFragment, + mirage::PUSH_CONSTANT_CUSTOM_OFFSET, + sizeof({{ fragment_struct_name }}), + &data + ); +} + +{%- endif %} +{%- endif %} + +// ========== 布局信息 ========== + +/** + * @brief {{ shader_name }} Push Constants 布局信息 + * + * 提供编译期可用的布局常量,用于验证和调试。 + */ +struct {{ shader_name }}PushConstantLayout { + /// 是否使用共享布局模式 + static constexpr bool is_shared_layout = {{ 'true' if is_shared_layout else 'false' }}; + + /// Header 区域大小(固定 16 字节) + static constexpr std::size_t header_size = mirage::PUSH_CONSTANT_HEADER_SIZE; + + /// Custom 区域起始偏移 + static constexpr std::size_t custom_offset = mirage::PUSH_CONSTANT_CUSTOM_OFFSET; + +{%- if has_vertex %} + /// 顶点着色器布局偏移量 + static constexpr std::size_t vertex_layout_offset = mirage::PUSH_CONSTANT_CUSTOM_OFFSET; + /// 顶点着色器 Custom 区域大小 + static constexpr std::size_t vertex_custom_size = {{ vertex_total_size }}; +{%- endif %} + +{%- if has_fragment %} + /// 片元着色器布局偏移量 + static constexpr std::size_t fragment_layout_offset = mirage::PUSH_CONSTANT_CUSTOM_OFFSET + {{ vertex_total_size if has_vertex else 0 }}; + /// 片元着色器 Custom 区域大小 + static constexpr std::size_t fragment_custom_size = {{ fragment_total_size }}; +{%- endif %} + +{%- if is_shared_layout and shared_member_count is defined %} + /// 共享成员数量 + static constexpr std::size_t shared_member_count = {{ shared_member_count }}; +{%- endif %} + + /// 总 Custom 区域大小(取较大值) + static constexpr std::size_t total_custom_size = {{ total_custom_size }}; +}; \ No newline at end of file diff --git a/tools/templates/push_constant/static_check.jinja2 b/tools/templates/push_constant/static_check.jinja2 index 39a41a5..861af96 100644 --- a/tools/templates/push_constant/static_check.jinja2 +++ b/tools/templates/push_constant/static_check.jinja2 @@ -1,27 +1,15 @@ -{# 生成 Push Constants 静态检查代码 #} +{# 生成 Push Constants 静态检查代码 (简化版) #} // ============ Push Constants 静态验证 ============ -// 编译期验证着色器声明的布局与简化 Push Constants 布局一致 +// 编译期验证着色器 Push Constants 布局 + +// 注意:supports_procedural_vertex_shader 常量已在 shader_spec 中定义 +// 检测条件:push_constant 第一个用户自定义成员偏移为 16 字节 +// 此处仅保留 Header 布局信息供调试参考 {% if is_standard %} -// 验证标准 Header 布局 (16 字节) -static_assert( - {{ struct_name }}_push_constant_header_layout::has_scale && - {{ struct_name }}_push_constant_header_layout::scale_offset == 0, - "{{ struct_name }}: scale must be at offset 0" -); - -static_assert( - {{ struct_name }}_push_constant_header_layout::has_translate && - {{ struct_name }}_push_constant_header_layout::translate_offset == 8, - "{{ struct_name }}: translate must be at offset 8" -); - -static_assert( - {{ struct_name }}_push_constant_header_layout::header_size == mirage::PUSH_CONSTANT_HEADER_SIZE, - "{{ struct_name }}: header size must be 16 bytes" -); +// 标准 Header 布局 (16 字节): scale + translate ✓ {% else %} -// 非标准 Header 布局 - 自定义验证(仅验证存在的成员) +// 非标准 Header 布局 {%- for member in header_members %} {%- if not member.is_valid %} // WARNING: {{ member.name }} layout mismatch diff --git a/tools/types.py b/tools/types.py index 4203997..83ab587 100644 --- a/tools/types.py +++ b/tools/types.py @@ -345,6 +345,93 @@ class PushConstantInfo: return self.effect_members +@dataclass +class StagePushConstantInfo: + """单个着色器阶段的 Push Constant 信息 + + 用于描述顶点或片元着色器中 Push Constant 的 Custom 区域布局。 + Custom 区域从偏移 16 字节开始(Header 之后)。 + + Attributes: + stage: 着色器阶段,"vertex" 或 "fragment" + members: Custom 区域的成员列表(偏移量已转换为相对于 Custom 区域) + relative_offset: 相对于 Custom 区域的起始偏移(从16开始) + total_size: Custom 区域的总大小(字节) + alignment: 结构体对齐要求 + """ + stage: str # "vertex" 或 "fragment" + members: List[MemberInfo] = field(default_factory=list) + relative_offset: int = PUSH_CONSTANT_HEADER_SIZE # 相对于 Custom 区域的起始偏移(从16开始) + total_size: int = 0 + alignment: int = 4 # 结构体对齐要求 + + @property + def has_members(self) -> bool: + """检查是否有 Custom 成员""" + return len(self.members) > 0 + + @property + def end_offset(self) -> int: + """计算 Custom 区域的结束偏移量""" + return self.relative_offset + self.total_size + + +@dataclass +class CombinedPushConstantInfo: + """合并后的双阶段 Push Constant 信息 + + 用于分析顶点和片元着色器的 Push Constant 布局关系, + 判断是否可以共享同一个结构体或需要分离处理。 + + 布局模式: + 1. 共享模式 (overlapping=True): 两个阶段使用相同的 Custom 区域布局 + 2. 分离模式 (overlapping=False): 两个阶段使用不同的 Custom 区域布局 + + Attributes: + vertex_info: 顶点着色器的 Push Constant 信息(可为 None) + fragment_info: 片元着色器的 Push Constant 信息(可为 None) + overlapping: 是否有重叠区域(共享模式) + shared_members: 共享的成员名称列表 + """ + vertex_info: Optional[StagePushConstantInfo] = None + fragment_info: Optional[StagePushConstantInfo] = None + + # 布局分析结果 + overlapping: bool = False # 是否有重叠区域(共享模式) + shared_members: List[str] = field(default_factory=list) # 共享的成员名称 + + @property + def has_vertex(self) -> bool: + """检查是否有顶点着色器的 Push Constant""" + return self.vertex_info is not None and self.vertex_info.has_members + + @property + def has_fragment(self) -> bool: + """检查是否有片元着色器的 Push Constant""" + return self.fragment_info is not None and self.fragment_info.has_members + + @property + def is_shared_layout(self) -> bool: + """检查是否使用共享布局模式""" + return self.overlapping and len(self.shared_members) > 0 + + @property + def vertex_only_members(self) -> List[str]: + """获取仅在顶点着色器中存在的成员名称""" + if not self.has_vertex: + return [] + vert_names = {m.name for m in self.vertex_info.members} + return [name for name in vert_names if name not in self.shared_members] + + @property + def fragment_only_members(self) -> List[str]: + """获取仅在片元着色器中存在的成员名称""" + if not self.has_fragment: + return [] + frag_names = {m.name for m in self.fragment_info.members} + return [name for name in frag_names if name not in self.shared_members] + + @dataclass class SPIRVReflection: """完整的SPIR-V反射信息"""