Skip to main content

lumen_engine/node/processing/
blur.rs

1use crate::node::{NodeId, NodeProperty, PortRef};
2
3use crate::gpu::{
4    BoundFrame, CompiledOutput, FrameBindContext, FrameBinding, GpuCompileNode, GpuFrameBindNode,
5    RasterHandle, compiler,
6};
7
8pub(crate) const SHADER: &str = include_str!("blur.wgsl");
9
10/// Applies a simple box blur to a raster.
11#[derive(Debug, Clone, lumen_macros::Node)]
12#[node(kind = "blur", name = "Blur", category = "processing")]
13pub struct Blur {
14    pub id: NodeId,
15    /// Blur radius in pixels.
16    #[property(kind = "float", min = 0, step = 0.5)]
17    pub radius: NodeProperty,
18    #[input()]
19    pub source: PortRef,
20}
21
22impl Default for Blur {
23    fn default() -> Self {
24        Self {
25            id: NodeId::new(0),
26            radius: NodeProperty::Float(4.0),
27            source: PortRef::empty(),
28        }
29    }
30}
31
32impl GpuCompileNode for Blur {
33    fn compile_gpu(
34        &self,
35        ctx: &mut crate::gpu::CompileContext<'_>,
36        port: &PortRef,
37    ) -> crate::Result<CompiledOutput> {
38        let (source, texture, params) = ctx.compile_unary_filter(
39            self.id,
40            &self.source,
41            port,
42            "blur",
43            SHADER,
44            std::mem::size_of::<compiler::BlurParams>() as u64,
45        )?;
46        ctx.push_frame_binding(FrameBinding::Blur {
47            node_id: self.id,
48            radius: self.radius.clone(),
49            buffer: params,
50        });
51        Ok(CompiledOutput::Raster(RasterHandle {
52            texture,
53            domain: source.domain,
54            metadata: source.metadata,
55        }))
56    }
57}
58
59impl GpuFrameBindNode for Blur {
60    fn bind_gpu_frame(
61        &self,
62        ctx: &FrameBindContext<'_>,
63        binding: &FrameBinding,
64        bound: &mut BoundFrame,
65    ) -> crate::Result<()> {
66        let FrameBinding::Blur {
67            node_id,
68            radius,
69            buffer,
70        } = binding
71        else {
72            return Ok(());
73        };
74        let radius = radius
75            .resolve_float(*node_id, "radius", &ctx.expr_context(*node_id, "radius"))?
76            .round()
77            .clamp(0.0, 32.0) as u32;
78        let params = compiler::BlurParams {
79            values: [radius, 0, 0, 0],
80        };
81        bound.write_buffer(*buffer, 0, bytemuck::bytes_of(&params));
82        Ok(())
83    }
84}