From fd125b495b18f34ee94acdb7e4ecaaacb5b9c12e Mon Sep 17 00:00:00 2001 From: daiqingshuang Date: Sat, 27 Dec 2025 12:15:45 +0800 Subject: [PATCH] 1 --- shaders/post_effect/blur.frag.glsl | 28 ++-- .../chromatic_aberration.frag.glsl | 35 +++-- shaders/post_effect/color_adjust.frag.glsl | 32 ++--- shaders/post_effect/color_tint.frag.glsl | 32 ++--- shaders/post_effect/noise.frag.glsl | 41 +++--- shaders/post_effect/vignette.frag.glsl | 32 ++--- .../custom_shader_quad_procedural.vert.glsl | 30 +++-- shaders/widget/glitch_effect.frag.glsl | 24 ++-- shaders/widget/wave.frag.glsl | 26 ++-- src/render/pipeline/render_pipeline.cpp | 52 ++++++++ src/render/pipeline/window_render_context.cpp | 126 ++++++++++++++++-- src/render/pipeline/window_render_context.h | 3 + .../custom_shader_widget_renderer.cpp | 56 ++++---- src/render/vulkan/swapchain.cpp | 14 +- src/ui/window/render_window.cpp | 19 +++ 15 files changed, 365 insertions(+), 185 deletions(-) diff --git a/shaders/post_effect/blur.frag.glsl b/shaders/post_effect/blur.frag.glsl index 37ada9e..2b71feb 100644 --- a/shaders/post_effect/blur.frag.glsl +++ b/shaders/post_effect/blur.frag.glsl @@ -5,24 +5,27 @@ // ============================================================================ // ============================================================================ -// 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: 效果参数 (本着色器使用) +// [0-15] Header: scale + translate +// [16-31] effect_rect (vec4) +// [32-47] original_effect_rect (vec4) +// [48-51] intensity (float) // +// 注意:顶点和片元着色器共享相同的 Push Constants 布局 // ============================================================================ layout(push_constant) uniform PushConstants { - // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 + // Header (16 bytes) [offset 0-15] vec2 scale; ///< 缩放因子 (2/width, 2/height) vec2 translate; ///< 平移因子 (-1, -1) - // === 片元阶段专用参数 (从 offset 52 开始) === - // 注意:跳过顶点阶段的 effect_rect(16) + original_effect_rect(16) + intensity(4) = 36 bytes [offset 16-51] - layout(offset = 52) float intensity; ///< [52-55] 效果强度 + // 用户自定义数据 (112 bytes) [offset 16-127] + vec4 effect_rect; ///< [16-31] 效果区域 [x, y, width, height] + vec4 original_effect_rect; ///< [32-47] 原始效果区域 [x, y, width, height] + float intensity; ///< [48-51] 效果强度 // 可扩展更多用户数据... } pc; @@ -34,8 +37,9 @@ layout(set = 0, binding = 1) uniform BlurParams { vec2 direction; ///< 模糊方向(用于分离式模糊) } blur; -// 来自顶点着色器(使用 post_process 模式的标准顶点着色器) -layout(location = 0) in vec2 v_uv; +// 来自顶点着色器 +layout(location = 0) in vec2 v_uv; ///< 控件局部 UV [0,1] +layout(location = 1) in vec2 v_screen_uv; ///< 屏幕空间 UV(用于采样源纹理) // 输出 layout(location = 0) out vec4 frag_color; @@ -53,8 +57,8 @@ void main() { vec2 tex_size = vec2(textureSize(u_source_texture, 0)); vec2 texel_size = 1.0 / tex_size; - // 使用 v_uv 计算源纹理坐标(v_uv 范围 [0,1],viewport_size 转换为纹理坐标) - vec2 source_uv = v_uv; + // 使用 v_screen_uv 采样源纹理(backdrop 模式) + vec2 source_uv = v_screen_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 555bc02..ea54b81 100644 --- a/shaders/post_effect/chromatic_aberration.frag.glsl +++ b/shaders/post_effect/chromatic_aberration.frag.glsl @@ -1,29 +1,23 @@ #version 450 core // ============================================================================ -// 通用输入 -// ============================================================================ - -// ============================================================================ -// 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: 效果参数 (本着色器使用) +// [0-15] Header: scale + translate +// [16-19] intensity (float) // +// 注意:顶点和片元着色器共享相同的 Push Constants 布局 // ============================================================================ layout(push_constant) uniform PushConstants { - // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 - vec2 scale; ///< 缩放因子 (2/width, 2/height) - vec2 translate; ///< 平移因子 (-1, -1) + // Header (16 bytes) [offset 0-15] + vec2 scale; ///< 缩放因子 + vec2 translate; ///< 平移因子 - // === 片元阶段专用参数 (从 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 计算) + // 用户自定义数据 (112 bytes) [offset 16-127] + float intensity; ///< [16-19] 效果强度 // 可扩展更多用户数据... } pc; @@ -32,8 +26,9 @@ layout(set = 0, binding = 1) uniform ChromaticAberrationParams { vec2 offset; ///< RGB 通道偏移 } ca; -layout(location = 0) in vec2 v_uv; -layout(location = 1) in vec2 v_screen_pos; +// 来自顶点着色器 +layout(location = 0) in vec2 v_uv; ///< 控件局部 UV [0,1] +layout(location = 1) in vec2 v_screen_uv; ///< 屏幕空间 UV(用于采样源纹理) layout(location = 0) out vec4 frag_color; @@ -42,8 +37,10 @@ layout(location = 0) out vec4 frag_color; // ============================================================================ void main() { - vec2 source_uv = v_screen_pos / pc.viewport_size; - vec2 texel_size = 1.0 / pc.viewport_size; + // 使用 v_screen_uv 采样源纹理(backdrop 模式) + vec2 source_uv = v_screen_uv; + vec2 tex_size = vec2(textureSize(u_source_texture, 0)); + vec2 texel_size = 1.0 / tex_size; // 计算偏移(从中心向外增强) vec2 center = vec2(0.5); diff --git a/shaders/post_effect/color_adjust.frag.glsl b/shaders/post_effect/color_adjust.frag.glsl index 25d3f65..a6fe789 100644 --- a/shaders/post_effect/color_adjust.frag.glsl +++ b/shaders/post_effect/color_adjust.frag.glsl @@ -1,29 +1,23 @@ #version 450 core // ============================================================================ -// 通用输入 -// ============================================================================ - -// ============================================================================ -// 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: 效果参数 (本着色器使用) +// [0-15] Header: scale + translate +// [16-19] intensity (float) // +// 注意:顶点和片元着色器共享相同的 Push Constants 布局 // ============================================================================ layout(push_constant) uniform PushConstants { - // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 - vec2 scale; ///< 缩放因子 (2/width, 2/height) - vec2 translate; ///< 平移因子 (-1, -1) + // Header (16 bytes) [offset 0-15] + vec2 scale; ///< 缩放因子 + vec2 translate; ///< 平移因子 - // === 片元阶段专用参数 (从 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 计算) + // 用户自定义数据 (112 bytes) [offset 16-127] + float intensity; ///< [16-19] 效果强度 // 可扩展更多用户数据... } pc; @@ -35,8 +29,9 @@ layout(set = 0, binding = 1) uniform ColorAdjustParams { float gamma; ///< 伽马 [0.1, 3] } color; -layout(location = 0) in vec2 v_uv; -layout(location = 1) in vec2 v_screen_pos; +// 来自顶点着色器 +layout(location = 0) in vec2 v_uv; ///< 控件局部 UV [0,1] +layout(location = 1) in vec2 v_screen_uv; ///< 屏幕空间 UV(用于采样源纹理) layout(location = 0) out vec4 frag_color; @@ -45,7 +40,8 @@ layout(location = 0) out vec4 frag_color; // ============================================================================ void main() { - vec2 source_uv = v_screen_pos / pc.viewport_size; + // 使用 v_screen_uv 采样源纹理(backdrop 模式) + vec2 source_uv = v_screen_uv; vec4 original = texture(u_source_texture, source_uv); vec3 c = original.rgb; diff --git a/shaders/post_effect/color_tint.frag.glsl b/shaders/post_effect/color_tint.frag.glsl index 2204ef4..c76200e 100644 --- a/shaders/post_effect/color_tint.frag.glsl +++ b/shaders/post_effect/color_tint.frag.glsl @@ -1,29 +1,23 @@ #version 450 core // ============================================================================ -// 通用输入 -// ============================================================================ - -// ============================================================================ -// 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: 效果参数 (本着色器使用) +// [0-15] Header: scale + translate +// [16-19] intensity (float) // +// 注意:顶点和片元着色器共享相同的 Push Constants 布局 // ============================================================================ layout(push_constant) uniform PushConstants { - // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 - vec2 scale; ///< 缩放因子 (2/width, 2/height) - vec2 translate; ///< 平移因子 (-1, -1) + // Header (16 bytes) [offset 0-15] + vec2 scale; ///< 缩放因子 + vec2 translate; ///< 平移因子 - // === 片元阶段专用参数 (从 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 计算) + // 用户自定义数据 (112 bytes) [offset 16-127] + float intensity; ///< [16-19] 效果强度 // 可扩展更多用户数据... } pc; @@ -33,8 +27,9 @@ layout(set = 0, binding = 1) uniform ColorTintParams { int blend_mode; ///< 混合模式 } tint; -layout(location = 0) in vec2 v_uv; -layout(location = 1) in vec2 v_screen_pos; +// 来自顶点着色器 +layout(location = 0) in vec2 v_uv; ///< 控件局部 UV [0,1] +layout(location = 1) in vec2 v_screen_uv; ///< 屏幕空间 UV(用于采样源纹理) layout(location = 0) out vec4 frag_color; @@ -67,7 +62,8 @@ vec3 blend_overlay(vec3 base, vec3 blend) { } void main() { - vec2 source_uv = v_screen_pos / pc.viewport_size; + // 使用 v_screen_uv 采样源纹理(backdrop 模式) + vec2 source_uv = v_screen_uv; vec4 original = texture(u_source_texture, source_uv); vec3 result = original.rgb; diff --git a/shaders/post_effect/noise.frag.glsl b/shaders/post_effect/noise.frag.glsl index 5b8f33a..058cc76 100644 --- a/shaders/post_effect/noise.frag.glsl +++ b/shaders/post_effect/noise.frag.glsl @@ -1,30 +1,25 @@ #version 450 core // ============================================================================ -// 通用输入 -// ============================================================================ - -// ============================================================================ -// 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: 效果参数 (本着色器使用) +// [0-15] Header: scale + translate +// [16-19] intensity (float) +// [20-23] time (float) - 用于动画噪点 // +// 注意:顶点和片元着色器共享相同的 Push Constants 布局 // ============================================================================ layout(push_constant) uniform PushConstants { - // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 - vec2 scale; ///< 缩放因子 (2/width, 2/height) - vec2 translate; ///< 平移因子 (-1, -1) + // Header (16 bytes) [offset 0-15] + vec2 scale; ///< 缩放因子 + vec2 translate; ///< 平移因子 - // === 片元阶段专用参数 (从 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] 时间(用于动画噪点) + // 用户自定义数据 (112 bytes) [offset 16-127] + float intensity; ///< [16-19] 效果强度 + float time; ///< [20-23] 时间(用于动画噪点) // 可扩展更多用户数据... } pc; @@ -35,8 +30,9 @@ layout(set = 0, binding = 1) uniform NoiseParams { int animated; ///< 是否动画 } noise; -layout(location = 0) in vec2 v_uv; -layout(location = 1) in vec2 v_screen_pos; +// 来自顶点着色器 +layout(location = 0) in vec2 v_uv; ///< 控件局部 UV [0,1] +layout(location = 1) in vec2 v_screen_uv; ///< 屏幕空间 UV(用于采样源纹理) layout(location = 0) out vec4 frag_color; @@ -50,11 +46,14 @@ float random(vec2 st) { } void main() { - vec2 source_uv = v_screen_pos / pc.viewport_size; + // 使用 v_screen_uv 采样源纹理(backdrop 模式) + vec2 source_uv = v_screen_uv; vec4 original = texture(u_source_texture, source_uv); - // 计算噪点坐标 - vec2 noise_uv = v_screen_pos / noise.grain_size; + // 计算噪点坐标(使用 v_uv 局部坐标计算噪点) + vec2 tex_size = vec2(textureSize(u_source_texture, 0)); + vec2 pixel_pos = v_screen_uv * tex_size; + vec2 noise_uv = pixel_pos / noise.grain_size; // 如果启用动画,添加时间偏移 float time_offset = noise.animated != 0 ? pc.time : 0.0; diff --git a/shaders/post_effect/vignette.frag.glsl b/shaders/post_effect/vignette.frag.glsl index f9b1d3e..c76cba7 100644 --- a/shaders/post_effect/vignette.frag.glsl +++ b/shaders/post_effect/vignette.frag.glsl @@ -1,29 +1,23 @@ #version 450 core // ============================================================================ -// 通用输入 -// ============================================================================ - -// ============================================================================ -// 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: 效果参数 (本着色器使用) +// [0-15] Header: scale + translate +// [16-19] intensity (float) // +// 注意:顶点和片元着色器共享相同的 Push Constants 布局 // ============================================================================ layout(push_constant) uniform PushConstants { - // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 - vec2 scale; ///< 缩放因子 (2/width, 2/height) - vec2 translate; ///< 平移因子 (-1, -1) + // Header (16 bytes) [offset 0-15] + vec2 scale; ///< 缩放因子 + vec2 translate; ///< 平移因子 - // === 片元阶段专用参数 (从 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 计算) + // 用户自定义数据 (112 bytes) [offset 16-127] + float intensity; ///< [16-19] 效果强度 // 可扩展更多用户数据... } pc; @@ -34,8 +28,9 @@ layout(set = 0, binding = 1) uniform VignetteParams { vec4 tint_color; ///< 暗角颜色 } vignette; -layout(location = 0) in vec2 v_uv; -layout(location = 1) in vec2 v_screen_pos; +// 来自顶点着色器 +layout(location = 0) in vec2 v_uv; ///< 控件局部 UV [0,1] +layout(location = 1) in vec2 v_screen_uv; ///< 屏幕空间 UV(用于采样源纹理) layout(location = 0) out vec4 frag_color; @@ -44,7 +39,8 @@ layout(location = 0) out vec4 frag_color; // ============================================================================ void main() { - vec2 source_uv = v_screen_pos / pc.viewport_size; + // 使用 v_screen_uv 采样源纹理(backdrop 模式) + vec2 source_uv = v_screen_uv; vec4 original = texture(u_source_texture, source_uv); // 计算到中心的距离(归一化) diff --git a/shaders/widget/custom_shader_quad_procedural.vert.glsl b/shaders/widget/custom_shader_quad_procedural.vert.glsl index 91de87a..de32dfd 100644 --- a/shaders/widget/custom_shader_quad_procedural.vert.glsl +++ b/shaders/widget/custom_shader_quad_procedural.vert.glsl @@ -12,6 +12,11 @@ * 1: 右上 (1,0) * 2: 左下 (0,1) * 3: 右下 (1,1) + * + * 变换说明: + * C++ 端预先计算 scale/translate 以包含控件位置和大小信息: + * scale = vec2(widget_width * 2 / viewport_width, widget_height * 2 / viewport_height) + * translate = vec2(-1 + widget_x * 2 / viewport_width, -1 + widget_y * 2 / viewport_height) */ // ============================================================================ @@ -20,20 +25,20 @@ layout(push_constant) uniform PushConstants { // 基础变换 (16 bytes) [offset 0-15] - vec2 scale; ///< 缩放因子 (2/width, 2/height) - vec2 translate; ///< 平移因子 (-1, -1) + // 已预计算包含控件位置和大小信息 + vec2 scale; ///< 缩放因子 (widget_size * 2 / viewport_size) + vec2 translate; ///< 平移因子 (-1 + widget_pos * 2 / viewport_size) // 用户自定义数据 (112 bytes) [offset 16-127] - vec2 widget_pos; ///< 控件位置: x, y - vec2 widget_size; ///< 控件尺寸: width, height - // 可扩展更多用户数据... + // 可由片元着色器自由使用... } pc; // ============================================================================ // 输出到片段着色器(标准化接口) // ============================================================================ -layout(location = 0) out vec2 v_uv; ///< [0,1] 标准化 UV 坐标 +layout(location = 0) out vec2 v_uv; ///< [0,1] 标准化 UV 坐标(控件局部) +layout(location = 1) out vec2 v_screen_uv; ///< [0,1] 屏幕空间 UV(用于采样源纹理/backdrop) // ============================================================================ // 常量定义 @@ -61,13 +66,14 @@ void main() { // 获取标准化局部坐标 [0,1] vec2 local_pos = QUAD_POSITIONS[quad_vertex]; - // 计算屏幕空间位置用于 NDC 转换 - vec2 screen_pos = pc.widget_pos + local_pos * pc.widget_size; - - // 输出 UV 坐标(与局部坐标相同) + // 输出局部 UV 坐标(用于效果计算) v_uv = local_pos; - // 转换到 NDC(使用简化的 scale/translate 变换) - vec2 ndc = screen_pos * pc.scale + pc.translate; + // 直接变换到 NDC(scale/translate 已预计算包含控件位置和大小) + vec2 ndc = local_pos * pc.scale + pc.translate; gl_Position = vec4(ndc, 0.0, 1.0); + + // 输出屏幕空间 UV(用于采样源纹理/backdrop) + // NDC [-1, 1] -> UV [0, 1] + v_screen_uv = (ndc + 1.0) * 0.5; } \ No newline at end of file diff --git a/shaders/widget/glitch_effect.frag.glsl b/shaders/widget/glitch_effect.frag.glsl index 06c21e9..403fbcd 100644 --- a/shaders/widget/glitch_effect.frag.glsl +++ b/shaders/widget/glitch_effect.frag.glsl @@ -18,27 +18,25 @@ */ // ============================================================================ -// Push Constants(双阶段布局) +// Push Constants (简化布局) // ============================================================================ // // 布局说明 (128 bytes): -// [0-15] Header: scale + translate (顶点着色器使用) -// [16-31] 顶点阶段 Custom: widget_pos + widget_size (顶点着色器使用) -// [32-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// [0-15] Header: scale + translate (顶点着色器使用,已预计算控件位置/大小) +// [16-127] 用户自定义数据: 效果参数 (本着色器使用) // // ============================================================================ layout(push_constant) uniform PushConstants { - // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 - vec2 scale; ///< 缩放因子 (2/width, 2/height) - vec2 translate; ///< 平移因子 (-1, -1) + // Header (16 bytes) [offset 0-15] + vec2 scale; ///< 缩放因子 (已预计算包含控件信息) + vec2 translate; ///< 平移因子 (已预计算包含控件信息) - // === 片元阶段专用参数 (从 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] 时间(用于动画效果) + // 用户自定义数据 (112 bytes) [offset 16-127] + float intensity; ///< [16-19] 故障强度 + float scan_line_freq; ///< [20-23] 扫描线频率 + float noise_amount; ///< [24-27] 噪声量 + float time; ///< [28-31] 时间(用于动画效果) // 可扩展更多用户数据... } pc; diff --git a/shaders/widget/wave.frag.glsl b/shaders/widget/wave.frag.glsl index fb3ac56..4373a7a 100644 --- a/shaders/widget/wave.frag.glsl +++ b/shaders/widget/wave.frag.glsl @@ -18,28 +18,26 @@ */ // ============================================================================ -// Push Constants(双阶段布局) +// Push Constants (简化布局) // ============================================================================ // // 布局说明 (128 bytes): -// [0-15] Header: scale + translate (顶点着色器使用) -// [16-31] 顶点阶段 Custom: widget_pos + widget_size (顶点着色器使用) -// [32-127] 片元阶段 Custom: 效果参数 (本着色器使用) +// [0-15] Header: scale + translate (顶点着色器使用,已预计算控件位置/大小) +// [16-127] 用户自定义数据: 效果参数 (本着色器使用) // // ============================================================================ layout(push_constant) uniform PushConstants { - // Header (16 bytes) [offset 0-15] - 可选声明,片元着色器可能不使用 - vec2 scale; ///< 缩放因子 (2/width, 2/height) - vec2 translate; ///< 平移因子 (-1, -1) + // Header (16 bytes) [offset 0-15] + vec2 scale; ///< 缩放因子 (已预计算包含控件信息) + vec2 translate; ///< 平移因子 (已预计算包含控件信息) - // === 片元阶段专用参数 (从 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] 时间(用于动画效果) + // 用户自定义数据 (112 bytes) [offset 16-127] + vec4 base_color; ///< [16-31] 次色/结束颜色 (r, g, b, a) + float frequency; ///< [32-35] 波浪频率 + float amplitude; ///< [36-39] 波浪振幅 + float speed; ///< [40-43] 波浪速度 + float time; ///< [44-47] 时间(用于动画效果) // 可扩展更多用户数据... } pc; diff --git a/src/render/pipeline/render_pipeline.cpp b/src/render/pipeline/render_pipeline.cpp index d98e38d..f6d80c9 100644 --- a/src/render/pipeline/render_pipeline.cpp +++ b/src/render/pipeline/render_pipeline.cpp @@ -310,6 +310,9 @@ namespace mirage { // 渲染实现 // ============================================================================ + // [DEBUG] 静态变量用于首帧诊断 + static bool first_frame_logged = false; + auto render_pipeline::render_window_impl( window_render_context& window, const render_tree& tree, @@ -333,6 +336,11 @@ namespace mirage { // 获取窗口独立的帧索引(用于窗口相关资源:fence、命令缓冲、描述符池) uint32_t frame_index = window.get_current_frame(); + + // [DEBUG] 首帧诊断日志 + if (!first_frame_logged) { + std::cout << "[DEBUG-首帧] render_window_impl 开始, frame_index=" << frame_index << std::endl; + } // 1. 等待帧栅栏(使用窗口独立的帧栅栏) if (!window.wait_for_frame(frame_index)) { @@ -367,6 +375,12 @@ namespace mirage { return std::unexpected(render_error::image_acquisition_failed); } uint32_t image_index = *maybe_image_index; + + // [DEBUG] 首帧诊断日志 + if (!first_frame_logged) { + std::cout << "[DEBUG-首帧] acquire_next_image 成功, image_index=" << image_index + << ", swapchain_image_count=" << window.get_swapchain().get_image_count() << std::endl; + } // 重置帧栅栏(使用窗口独立的帧栅栏) window.reset_frame_fence(frame_index); @@ -516,6 +530,13 @@ namespace mirage { .linear_sampler = shared_resources_->get_blit_sampler() }; + // [DEBUG] 首帧渲染树诊断 + if (!first_frame_logged) { + std::cout << "[DEBUG-TREE] 渲染树空=" << (tree.empty() ? "true" : "false") + << ", viewport_size: " << viewport_size.x() << "x" << viewport_size.y() + << ", use_full_render: " << (use_full_render ? "true" : "false") << std::endl; + } + auto* tree_executor = shared_resources_->get_tree_executor(); tree_executor->execute(tree, exec_ctx); @@ -578,7 +599,20 @@ namespace mirage { } // 23. Present + // [DEBUG] 首帧诊断日志 + if (!first_frame_logged) { + std::cout << "[DEBUG-首帧] 即将 Present, image_index=" << image_index + << ", frame_index=" << frame_index << std::endl; + } + auto present_result = window.present(device_.get_present_queue(), image_index); + + // [DEBUG] 首帧诊断日志 + if (!first_frame_logged) { + std::cout << "[DEBUG-首帧] Present 完成, result=" << vk::to_string(present_result) << std::endl; + first_frame_logged = true; + } + if (present_result == vk::Result::eErrorOutOfDateKHR || present_result == vk::Result::eSuboptimalKHR) { return std::unexpected(render_error::swapchain_out_of_date); @@ -597,6 +631,9 @@ namespace mirage { // Blit 到 Swapchain // ============================================================================ + // [DEBUG] 静态变量用于 blit 诊断 + static bool blit_first_frame_logged = false; + void render_pipeline::blit_to_swapchain( frame_context& ctx, window_render_context& window, @@ -610,6 +647,15 @@ namespace mirage { // 设置视口和裁剪 auto extent = window.get_extent(); + + // [DEBUG] 首帧 blit 诊断 + if (!blit_first_frame_logged) { + auto& offscreen = window.get_offscreen(); + auto offscreen_extent = offscreen.extent(); + std::cout << "[DEBUG-BLIT] 首帧 blit - window extent: " << extent.width << "x" << extent.height + << ", offscreen extent: " << offscreen_extent.width << "x" << offscreen_extent.height + << ", offscreen state: " << static_cast(offscreen.current_state()) << std::endl; + } vk::Viewport viewport{}; viewport.x = 0.0f; @@ -666,6 +712,12 @@ namespace mirage { } } + // [DEBUG] 首帧 blit 完成 + if (!blit_first_frame_logged) { + std::cout << "[DEBUG-BLIT] 首帧 blit draw 完成" << std::endl; + blit_first_frame_logged = true; + } + // 绘制全屏三角形 cmd.draw(3, 1, 0, 0); } diff --git a/src/render/pipeline/window_render_context.cpp b/src/render/pipeline/window_render_context.cpp index e6503dc..ccf8580 100644 --- a/src/render/pipeline/window_render_context.cpp +++ b/src/render/pipeline/window_render_context.cpp @@ -185,11 +185,14 @@ namespace mirage { return false; } swapchain_ = std::make_unique(std::move(swapchain_result.value())); + + std::cout << "[DEBUG-WRC] 首次创建 swapchain 完成, 请求尺寸: " << width << "x" << height << std::endl; } // 获取实际的 swapchain 尺寸 // 注意:如果窗口最小化,extent 可能为 (0, 0) auto actual_extent = swapchain_->get_extent(); + std::cout << "[DEBUG-WRC] swapchain 实际尺寸: " << actual_extent.width << "x" << actual_extent.height << std::endl; if (actual_extent.width == 0 || actual_extent.height == 0) { // 窗口最小化,跳过资源创建,视为成功 return true; @@ -223,15 +226,16 @@ namespace mirage { } else { // 首次创建离屏目标(必须启用深度/模板缓冲以匹配渲染器管线的 RenderPass) - offscreen_ = std::make_unique( - *device_, *res_mgr_, - offscreen_target::config{ - .enable_depth_stencil = true, - .depth_stencil_format = vk::Format::eD24UnormS8Uint - } - ); - offscreen_->initialize(actual_extent.width, actual_extent.height); - } + offscreen_ = std::make_unique( + *device_, *res_mgr_, + offscreen_target::config{ + .enable_depth_stencil = true, + .depth_stencil_format = vk::Format::eD24UnormS8Uint + } + ); + offscreen_->initialize(actual_extent.width, actual_extent.height); + std::cout << "[DEBUG-WRC] 首次创建 offscreen target, 尺寸: " << actual_extent.width << "x" << actual_extent.height << std::endl; + } // 创建 blit 描述符(如果尚未创建) if (blit_descriptor_sets_.empty() && blit_desc_layout_) { @@ -252,6 +256,13 @@ namespace mirage { // 重置图像状态 reset_all_image_states(); + + // [修复] 首次创建时预初始化所有 swapchain 图像 + // 在三缓冲模式下,窗口系统可能显示未被渲染的图像导致黑屏 + // 通过预先 clear 所有图像来解决此问题 + if (swapchain_) { + prewarm_swapchain_images(); + } return true; } @@ -886,4 +897,101 @@ namespace mirage { surface_ = nullptr; } + // ============================================================================ + // 预热 swapchain 图像(解决三缓冲首帧黑屏问题) + // ============================================================================ + + auto window_render_context::prewarm_swapchain_images() -> void { + if (!device_ || !swapchain_ || !res_mgr_ || command_buffers_.empty()) { + return; + } + + auto device_handle = device_->get_handle(); + auto image_count = swapchain_->get_image_count(); + auto extent = swapchain_->get_extent(); + + std::cout << "[WINDOW_RENDER_CONTEXT] 预热 " << image_count << " 张 swapchain 图像..." << std::endl; + + // 获取 RenderPass(使用 clear 模式,因为它接受 undefined 布局) + vk::RenderPass render_pass = get_swapchain_render_pass(true); + + // 为每张图像执行一次 clear 操作 + for (uint32_t i = 0; i < image_count; ++i) { + // 分配一个临时命令缓冲 + vk::CommandBufferAllocateInfo alloc_info{}; + alloc_info.commandPool = command_pool_.get(); + alloc_info.level = vk::CommandBufferLevel::ePrimary; + alloc_info.commandBufferCount = 1; + + auto [alloc_result, cmd_buffers] = device_handle.allocateCommandBuffers(alloc_info); + if (alloc_result != vk::Result::eSuccess || cmd_buffers.empty()) { + std::cerr << "[WINDOW_RENDER_CONTEXT] 预热失败:无法分配命令缓冲" << std::endl; + return; + } + auto cmd = cmd_buffers[0]; + + // 开始录制 + vk::CommandBufferBeginInfo begin_info{}; + begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit; + if (cmd.begin(begin_info) != vk::Result::eSuccess) { + device_handle.freeCommandBuffers(command_pool_.get(), cmd); + continue; + } + + // 开始 RenderPass(这会自动处理 undefined -> color attachment 的布局转换) + vk::RenderPassBeginInfo rp_begin{}; + rp_begin.renderPass = render_pass; + rp_begin.framebuffer = framebuffers_[i].get(); + rp_begin.renderArea.offset = vk::Offset2D{0, 0}; + rp_begin.renderArea.extent = extent; + + // 使用透明黑色 clear(与正常渲染一致) + vk::ClearValue clear_value{}; + clear_value.color = vk::ClearColorValue{std::array{0.0f, 0.0f, 0.0f, 0.0f}}; + rp_begin.clearValueCount = 1; + rp_begin.pClearValues = &clear_value; + + cmd.beginRenderPass(rp_begin, vk::SubpassContents::eInline); + cmd.endRenderPass(); + + // 结束录制 + if (cmd.end() != vk::Result::eSuccess) { + device_handle.freeCommandBuffers(command_pool_.get(), cmd); + continue; + } + + // 创建临时 fence 用于同步 + vk::FenceCreateInfo fence_info{}; + auto [fence_result, fence] = device_handle.createFence(fence_info); + if (fence_result != vk::Result::eSuccess) { + device_handle.freeCommandBuffers(command_pool_.get(), cmd); + continue; + } + + // 提交命令 + vk::SubmitInfo submit_info{}; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &cmd; + + auto submit_result = device_->get_graphics_queue().submit(1, &submit_info, fence); + if (submit_result != vk::Result::eSuccess) { + device_handle.destroyFence(fence); + device_handle.freeCommandBuffers(command_pool_.get(), cmd); + continue; + } + + // 等待完成 + (void)device_handle.waitForFences(1, &fence, VK_TRUE, UINT64_MAX); + + // 清理 + device_handle.destroyFence(fence); + device_handle.freeCommandBuffers(command_pool_.get(), cmd); + + // 标记图像已初始化 + mark_image_initialized(i); + } + + std::cout << "[WINDOW_RENDER_CONTEXT] 预热完成" << std::endl; + } + } // namespace mirage diff --git a/src/render/pipeline/window_render_context.h b/src/render/pipeline/window_render_context.h index a09e23c..e08704f 100644 --- a/src/render/pipeline/window_render_context.h +++ b/src/render/pipeline/window_render_context.h @@ -299,6 +299,9 @@ namespace mirage { /// 销毁 swapchain 相关资源(Framebuffer、RenderPass) auto destroy_swapchain_resources() -> void; + + /// 预热 swapchain 图像(解决三缓冲首帧黑屏问题) + auto prewarm_swapchain_images() -> void; // ===================================================================== // 成员变量 diff --git a/src/render/renderers/custom_shader_widget_renderer.cpp b/src/render/renderers/custom_shader_widget_renderer.cpp index 8c222b8..6557c2d 100644 --- a/src/render/renderers/custom_shader_widget_renderer.cpp +++ b/src/render/renderers/custom_shader_widget_renderer.cpp @@ -320,10 +320,32 @@ namespace mirage { // ======================================== // Header [0-15]: scale + translate (16 字节) // ======================================== - // NDC = position * scale + translate + // 对于使用程序化四边形的模式(procedural 和 post_process),预计算包含控件位置和大小的 scale/translate: + // scale = (widget_width * 2 / viewport_width, widget_height * 2 / viewport_height) + // translate = (-1 + widget_x * 2 / viewport_width, -1 + widget_y * 2 / viewport_height) + // + // 对于 custom_geometry 模式,使用标准的全局变换: // scale = (2/width, 2/height) // translate = (-1, -1) - pc->header = create_push_constants_header(viewport_size_.x(), viewport_size_.y()); + if (command.render_mode == custom_shader_render_mode::procedural || + command.render_mode == custom_shader_render_mode::post_process || + command.render_mode == custom_shader_render_mode::mask) { + // 程序化四边形模式:预计算控件的 NDC 变换 + // 顶点着色器将使用 local_pos * scale + translate 来生成 NDC + float scale_x = command.size.x() * 2.0f / viewport_size_.x(); + float scale_y = command.size.y() * 2.0f / viewport_size_.y(); + float translate_x = -1.0f + command.position.x() * 2.0f / viewport_size_.x(); + float translate_y = -1.0f + command.position.y() * 2.0f / viewport_size_.y(); + + pc->header.scale[0] = scale_x; + pc->header.scale[1] = scale_y; + pc->header.translate[0] = translate_x; + pc->header.translate[1] = translate_y; + } + else { + // custom_geometry 模式:使用标准的全局变换(顶点数据已包含屏幕坐标) + pc->header = create_push_constants_header(viewport_size_.x(), viewport_size_.y()); + } // ======================================== // Custom [16-127]: 用户自定义数据 (112 字节) @@ -340,11 +362,8 @@ namespace mirage { } else { // ======================================== - // 单一模式(向后兼容) + // 简化模式:用户数据直接从 offset 16 开始 // ======================================== - // 布局: - // [16-31]: 几何信息 (widget_pos + widget_size) - 16 字节,顶点着色器必需 - // [32-127]: 用户自定义数据 - 96 字节 fill_legacy_push_constants(command, pc); } } @@ -353,26 +372,17 @@ namespace mirage { const custom_shader_widget_command& command, custom_shader_push_constants* pc) const { // ======================================== - // 单一模式布局(向后兼容) - // [16-31]: 几何信息 (widget_pos + widget_size) - 16 字节 - // [32-127]: 用户自定义数据 - 96 字节 + // 简化布局:用户数据直接从 offset 16 开始 + // [16-127]: 用户自定义数据 - 112 字节 // ======================================== + // 注意:widget_pos 和 widget_size 已通过 scale/translate 预计算 + // 不再需要在 custom_data 中传递几何信息 - // 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 - + // 直接复制用户数据到 offset 16 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); + size_t copy_size = std::min(static_cast(command.push_constant_size), + static_cast(PUSH_CONSTANT_CUSTOM_MAX_SIZE)); + std::memcpy(pc->custom_data, command.push_constant_data, copy_size); } } diff --git a/src/render/vulkan/swapchain.cpp b/src/render/vulkan/swapchain.cpp index 12871e7..f4f33d3 100644 --- a/src/render/vulkan/swapchain.cpp +++ b/src/render/vulkan/swapchain.cpp @@ -265,14 +265,12 @@ namespace mirage { if (capabilities.currentExtent.width != std::numeric_limits::max()) { return capabilities.currentExtent; } - else { - vk::Extent2D actual_extent = {width, height}; - actual_extent.width = std::clamp(actual_extent.width, capabilities.minImageExtent.width, - capabilities.maxImageExtent.width); - actual_extent.height = std::clamp(actual_extent.height, capabilities.minImageExtent.height, - capabilities.maxImageExtent.height); - return actual_extent; - } + vk::Extent2D actual_extent = {width, height}; + actual_extent.width = std::clamp(actual_extent.width, capabilities.minImageExtent.width, + capabilities.maxImageExtent.width); + actual_extent.height = std::clamp(actual_extent.height, capabilities.minImageExtent.height, + capabilities.maxImageExtent.height); + return actual_extent; } auto swapchain::create( diff --git a/src/ui/window/render_window.cpp b/src/ui/window/render_window.cpp index 92e4e30..34650d1 100644 --- a/src/ui/window/render_window.cpp +++ b/src/ui/window/render_window.cpp @@ -111,6 +111,9 @@ namespace mirage { auto fb_size = window_->get_framebuffer_size(); current_width_ = static_cast(fb_size.x()); current_height_ = static_cast(fb_size.y()); + + std::cout << "[DEBUG-INIT] 窗口配置尺寸: " << config.width << "x" << config.height + << ", 帧缓冲尺寸: " << fb_size.x() << "x" << fb_size.y() << std::endl; // ======================================================================== // 3. 注册窗口到渲染管线 @@ -284,6 +287,9 @@ namespace mirage { } } + // [DEBUG] 静态变量用于首帧诊断 + static bool render_first_frame_logged = false; + void render_window::render() { auto* coordinator = context_provider_.get_thread_coordinator(); if (!coordinator || !root_widget_) { @@ -291,6 +297,12 @@ namespace mirage { << ", root_widget=" << root_widget_.get() << std::endl; return; } + + // [DEBUG] 首帧诊断日志 + if (!render_first_frame_logged) { + std::cout << "[DEBUG-RENDER] 首帧渲染 - current_size: " << current_width_ << "x" << current_height_ + << std::endl; + } // 检查是否真的需要渲染(基于脏控件计数) // 只有当有脏控件、或有待处理字形、或需要完全重绘时才提交帧 @@ -314,6 +326,13 @@ namespace mirage { // 统一使用多线程模式提交帧(传递 shared_ptr 确保布局期间有效) coordinator->submit_frame(win_id, root_widget_, viewport_size); + + // [DEBUG] 首帧诊断日志 + if (!render_first_frame_logged) { + std::cout << "[DEBUG-RENDER] 提交帧完成 - viewport_size: " << viewport_size.x() << "x" << viewport_size.y() + << std::endl; + render_first_frame_logged = true; + } // 提交后重置完全重绘标记 needs_full_redraw_ = false;