cvkg-render-gpu 0.3.2

Cyber Viking Kvasir Graph (CVKG) - High-fidelity agentic UI framework
Documentation
// =============================================================================
// SVG Filter: Separable Gaussian Blur
// =============================================================================
// Two-pass separable Gaussian blur for feGaussianBlur.
// Pass 1: Horizontal blur (read from input, write to temp)
// Pass 2: Vertical blur (read from temp, write to output)
//
// Uniforms:
//   params.xy = input texture dimensions (for texel size)
//   params.z  = standard deviation
//   params.w  = direction (0 = horizontal, 1 = vertical)
// =============================================================================

struct BlurUniforms {
    params: vec4<f32>,  // xy = texture size, z = std_deviation, w = direction
    kernel_size: u32,
    _pad0: u32,
    _pad1: u32,
    _pad2: u32,
};

@group(0) @binding(0) var<uniform> blur: BlurUniforms;
@group(0) @binding(1) var t_input: texture_2d<f32>;
@group(0) @binding(2) var s_input: sampler;

struct VertexOutput {
    @builtin(position) position: vec4<f32>,
    @location(0) texcoord: vec2<f32>,
};

@vertex
fn vs_filter(@builtin(vertex_index) idx: u32) -> VertexOutput {
    var out: VertexOutput;
    let x = f32(i32(idx) / 2) * 4.0 - 1.0;
    let y = f32(i32(idx) % 2) * 4.0 - 1.0;
    out.position = vec4<f32>(x, y, 0.0, 1.0);
    out.texcoord = vec2<f32>((x + 1.0) * 0.5, (1.0 - y) * 0.5);
    return out;
}

@fragment
fn fs_gaussian_blur(in: VertexOutput) -> @location(0) vec4<f32> {
    let texel = vec2<f32>(1.0 / blur.params.x, 1.0 / blur.params.y);
    let sigma = max(blur.params.z, 0.5);
    let kernel_radius = min(i32(sigma * 3.0), 16);

    var result = vec4<f32>(0.0);
    var weight_sum = 0.0;

    let is_vertical = blur.params.w > 0.5;
    let dir = select(vec2<f32>(1.0, 0.0), vec2<f32>(0.0, 1.0), is_vertical);

    for (var i: i32 = -kernel_radius; i <= kernel_radius; i++) {
        let offset = dir * f32(i) * texel;
        let weight = exp(-0.5 * f32(i * i) / (sigma * sigma));
        result += textureSample(t_input, s_input, in.texcoord + offset) * weight;
        weight_sum += weight;
    }

    return result / weight_sum;
}