From de7514e6b9ff9c0b9320975ba7d44754b5115b54 Mon Sep 17 00:00:00 2001 From: skal Date: Sat, 14 Feb 2026 15:23:14 +0100 Subject: refactor(wgsl): replace inline fullscreen_vs with common include Replace duplicate fullscreen triangle vertex shader code with #include "render/fullscreen_vs" in 8 workspace shaders. Eliminates ~60 lines of duplication and establishes single source of truth. Modified shaders: - circle_mask_compute.wgsl (main/test) - circle_mask_render.wgsl (main/test) - ellipse.wgsl (main/test) - gaussian_blur.wgsl (main/test) Updated test_shader_assets.cc to validate include directive instead of inline @vertex keyword for affected shaders. All tests passing (34/34). handoff(Claude): Shader modularization - fullscreen_vs consolidated Co-Authored-By: Claude Sonnet 4.5 --- doc/COMPLETED.md | 15 +++++++++++++++ src/tests/gpu/test_shader_assets.cc | 11 ++++++----- workspaces/main/shaders/circle_mask_compute.wgsl | 12 ++---------- workspaces/main/shaders/circle_mask_render.wgsl | 11 +---------- workspaces/main/shaders/ellipse.wgsl | 10 +--------- workspaces/main/shaders/gaussian_blur.wgsl | 11 ++--------- workspaces/test/shaders/circle_mask_compute.wgsl | 12 ++---------- workspaces/test/shaders/circle_mask_render.wgsl | 11 +---------- workspaces/test/shaders/ellipse.wgsl | 10 +--------- workspaces/test/shaders/gaussian_blur.wgsl | 11 ++--------- 10 files changed, 33 insertions(+), 81 deletions(-) diff --git a/doc/COMPLETED.md b/doc/COMPLETED.md index c7b2cae..dddcc3e 100644 --- a/doc/COMPLETED.md +++ b/doc/COMPLETED.md @@ -29,6 +29,21 @@ Detailed historical documents have been moved to `doc/archive/` for reference: Use `read @doc/archive/FILENAME.md` to access archived documents. +## Recently Completed (February 14, 2026) + +- [x] **Fullscreen Vertex Shader Modularization** + - **Goal**: Eliminate duplicate fullscreen triangle vertex shader code + - **Changes**: + - Replaced inline `vs_main()` with `#include "render/fullscreen_vs"` in 8 shaders + - Updated test_shader_assets.cc validation for ELLIPSE and GAUSSIAN_BLUR + - Modified shaders: circle_mask_compute, circle_mask_render, ellipse, gaussian_blur (main/test workspaces) + - **Impact**: + - ~60 lines eliminated across codebase + - Better maintainability: single source of truth for fullscreen vertex shader + - Consistent with existing pattern (solarize, distort, chroma_aberration, vignette) + - **Result**: 34/34 tests passing (100%) + - **Related**: Task #50 (WGSL Modularization) + ## Recently Completed (February 11, 2026) - [x] **Effect Render API Refactor** diff --git a/src/tests/gpu/test_shader_assets.cc b/src/tests/gpu/test_shader_assets.cc index 7f2811e..1736dc0 100644 --- a/src/tests/gpu/test_shader_assets.cc +++ b/src/tests/gpu/test_shader_assets.cc @@ -63,13 +63,14 @@ int main() { all_passed &= validate_shader(AssetId::ASSET_SHADER_PASSTHROUGH, "PASSTHROUGH", {"@vertex", "vs_main", "@fragment", "fs_main"}); - all_passed &= validate_shader(AssetId::ASSET_SHADER_ELLIPSE, "ELLIPSE", - {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader( + AssetId::ASSET_SHADER_ELLIPSE, "ELLIPSE", + {"#include \"render/fullscreen_vs\"", "@fragment", "fs_main"}); all_passed &= validate_shader(AssetId::ASSET_SHADER_PARTICLE_SPRAY_COMPUTE, "PARTICLE_SPRAY_COMPUTE", {"@compute", "main"}); - all_passed &= - validate_shader(AssetId::ASSET_SHADER_GAUSSIAN_BLUR, "GAUSSIAN_BLUR", - {"@vertex", "vs_main", "@fragment", "fs_main"}); + all_passed &= validate_shader( + AssetId::ASSET_SHADER_GAUSSIAN_BLUR, "GAUSSIAN_BLUR", + {"#include \"render/fullscreen_vs\"", "@fragment", "fs_main"}); all_passed &= validate_shader( AssetId::ASSET_SHADER_SOLARIZE, "SOLARIZE", {"#include \"render/fullscreen_vs\"", "@fragment", "fs_main"}); diff --git a/workspaces/main/shaders/circle_mask_compute.wgsl b/workspaces/main/shaders/circle_mask_compute.wgsl index 484d3dd..b167781 100644 --- a/workspaces/main/shaders/circle_mask_compute.wgsl +++ b/workspaces/main/shaders/circle_mask_compute.wgsl @@ -2,6 +2,8 @@ // Generates a circular mask (1.0 inside, 0.0 outside) #include "common_uniforms" +#include "render/fullscreen_vs" + struct CircleMaskParams { radius: f32, _pad0: f32, @@ -12,16 +14,6 @@ struct CircleMaskParams { @group(0) @binding(0) var uniforms: CommonUniforms; @group(0) @binding(1) var params: CircleMaskParams; -struct VSOutput { - @builtin(position) position: vec4, -}; - -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> VSOutput { - var pos = array, 3>( - vec2(-1, -1), vec2(3, -1), vec2(-1, 3)); - return VSOutput(vec4(pos[i], 0.0, 1.0)); -} - @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { let uv = p.xy / uniforms.resolution; let center = vec2(0.5, 0.5); diff --git a/workspaces/main/shaders/circle_mask_render.wgsl b/workspaces/main/shaders/circle_mask_render.wgsl index cfa002e..55620c1 100644 --- a/workspaces/main/shaders/circle_mask_render.wgsl +++ b/workspaces/main/shaders/circle_mask_render.wgsl @@ -5,19 +5,10 @@ @group(0) @binding(1) var mask_sampler: sampler; #include "common_uniforms" +#include "render/fullscreen_vs" @group(0) @binding(2) var uniforms: CommonUniforms; -struct VSOutput { - @builtin(position) position: vec4, -}; - -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> VSOutput { - var pos = array, 3>( - vec2(-1, -1), vec2(3, -1), vec2(-1, 3)); - return VSOutput(vec4(pos[i], 0.0, 1.0)); -} - @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { let uv = p.xy / uniforms.resolution; let mask_value = textureSample(mask_tex, mask_sampler, uv).r; diff --git a/workspaces/main/shaders/ellipse.wgsl b/workspaces/main/shaders/ellipse.wgsl index 69b2712..07f0c9a 100644 --- a/workspaces/main/shaders/ellipse.wgsl +++ b/workspaces/main/shaders/ellipse.wgsl @@ -1,16 +1,8 @@ #include "common_uniforms" +#include "render/fullscreen_vs" @group(0) @binding(0) var uniforms: CommonUniforms; -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4 { - var pos = array, 3>( - vec2(-1.0, -1.0), - vec2(3.0, -1.0), - vec2(-1.0, 3.0) - ); - return vec4(pos[i], 0.0, 1.0); -} - fn sdEllipse(p: vec2, ab: vec2) -> f32 { var p_abs = abs(p); if (p_abs.x > p_abs.y) { diff --git a/workspaces/main/shaders/gaussian_blur.wgsl b/workspaces/main/shaders/gaussian_blur.wgsl index 02156f7..22e467f 100644 --- a/workspaces/main/shaders/gaussian_blur.wgsl +++ b/workspaces/main/shaders/gaussian_blur.wgsl @@ -2,6 +2,8 @@ @group(0) @binding(1) var txt: texture_2d; #include "common_uniforms" +#include "render/fullscreen_vs" + struct GaussianBlurParams { strength: f32, _pad: f32, @@ -10,15 +12,6 @@ struct GaussianBlurParams { @group(0) @binding(2) var uniforms: CommonUniforms; @group(0) @binding(3) var params: GaussianBlurParams; -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4 { - var pos = array, 3>( - vec2(-1, -1), - vec2(3, -1), - vec2(-1, 3) - ); - return vec4(pos[i], 0.0, 1.0); -} - @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { let uv = p.xy / uniforms.resolution; var res = vec4(0.0); diff --git a/workspaces/test/shaders/circle_mask_compute.wgsl b/workspaces/test/shaders/circle_mask_compute.wgsl index 484d3dd..b167781 100644 --- a/workspaces/test/shaders/circle_mask_compute.wgsl +++ b/workspaces/test/shaders/circle_mask_compute.wgsl @@ -2,6 +2,8 @@ // Generates a circular mask (1.0 inside, 0.0 outside) #include "common_uniforms" +#include "render/fullscreen_vs" + struct CircleMaskParams { radius: f32, _pad0: f32, @@ -12,16 +14,6 @@ struct CircleMaskParams { @group(0) @binding(0) var uniforms: CommonUniforms; @group(0) @binding(1) var params: CircleMaskParams; -struct VSOutput { - @builtin(position) position: vec4, -}; - -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> VSOutput { - var pos = array, 3>( - vec2(-1, -1), vec2(3, -1), vec2(-1, 3)); - return VSOutput(vec4(pos[i], 0.0, 1.0)); -} - @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { let uv = p.xy / uniforms.resolution; let center = vec2(0.5, 0.5); diff --git a/workspaces/test/shaders/circle_mask_render.wgsl b/workspaces/test/shaders/circle_mask_render.wgsl index cfa002e..55620c1 100644 --- a/workspaces/test/shaders/circle_mask_render.wgsl +++ b/workspaces/test/shaders/circle_mask_render.wgsl @@ -5,19 +5,10 @@ @group(0) @binding(1) var mask_sampler: sampler; #include "common_uniforms" +#include "render/fullscreen_vs" @group(0) @binding(2) var uniforms: CommonUniforms; -struct VSOutput { - @builtin(position) position: vec4, -}; - -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> VSOutput { - var pos = array, 3>( - vec2(-1, -1), vec2(3, -1), vec2(-1, 3)); - return VSOutput(vec4(pos[i], 0.0, 1.0)); -} - @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { let uv = p.xy / uniforms.resolution; let mask_value = textureSample(mask_tex, mask_sampler, uv).r; diff --git a/workspaces/test/shaders/ellipse.wgsl b/workspaces/test/shaders/ellipse.wgsl index 69b2712..07f0c9a 100644 --- a/workspaces/test/shaders/ellipse.wgsl +++ b/workspaces/test/shaders/ellipse.wgsl @@ -1,16 +1,8 @@ #include "common_uniforms" +#include "render/fullscreen_vs" @group(0) @binding(0) var uniforms: CommonUniforms; -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4 { - var pos = array, 3>( - vec2(-1.0, -1.0), - vec2(3.0, -1.0), - vec2(-1.0, 3.0) - ); - return vec4(pos[i], 0.0, 1.0); -} - fn sdEllipse(p: vec2, ab: vec2) -> f32 { var p_abs = abs(p); if (p_abs.x > p_abs.y) { diff --git a/workspaces/test/shaders/gaussian_blur.wgsl b/workspaces/test/shaders/gaussian_blur.wgsl index 02156f7..22e467f 100644 --- a/workspaces/test/shaders/gaussian_blur.wgsl +++ b/workspaces/test/shaders/gaussian_blur.wgsl @@ -2,6 +2,8 @@ @group(0) @binding(1) var txt: texture_2d; #include "common_uniforms" +#include "render/fullscreen_vs" + struct GaussianBlurParams { strength: f32, _pad: f32, @@ -10,15 +12,6 @@ struct GaussianBlurParams { @group(0) @binding(2) var uniforms: CommonUniforms; @group(0) @binding(3) var params: GaussianBlurParams; -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4 { - var pos = array, 3>( - vec2(-1, -1), - vec2(3, -1), - vec2(-1, 3) - ); - return vec4(pos[i], 0.0, 1.0); -} - @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { let uv = p.xy / uniforms.resolution; var res = vec4(0.0); -- cgit v1.2.3