Skip to main content

lumen_engine/node/source/
background.rs

1use crate::node::{NodeId, NodeParamEvalContext, NodeParams, vector::paint::Paint};
2
3use crate::gpu::{
4    BoundFrame, CompiledOutput, FrameBindContext, GpuCompileNode, GpuCompiledNode, RasterHandle,
5    RasterMetadata, compiler,
6};
7
8pub(crate) const SHADER: &str = include_str!("background.wgsl");
9
10#[derive(Debug, Clone, lumen_macros::Delegate)]
11pub struct BackgroundParams {
12    /// Background paint.
13    #[meta()]
14    pub paint: Paint,
15    /// Output width in pixels. Use 0 to match the composition width.
16    #[meta(min = 0, step = 1)]
17    pub width: u32,
18    /// Output height in pixels. Use 0 to match the composition height.
19    #[meta(min = 0, step = 1)]
20    pub height: u32,
21}
22
23impl Default for BackgroundParams {
24    fn default() -> Self {
25        Self {
26            paint: Paint::solid([0, 0, 0, 255]),
27            width: 0,
28            height: 0,
29        }
30    }
31}
32
33/// Generates a background raster texture.
34#[derive(Debug, Clone, lumen_macros::Node)]
35#[node(kind = "background", name = "Background", category = "source")]
36pub struct Background {
37    pub id: NodeId,
38    #[params]
39    pub params: BackgroundParamsDelegate,
40}
41
42impl Default for Background {
43    fn default() -> Self {
44        Self {
45            id: NodeId::new(0),
46            params: BackgroundParamsDelegate::default(),
47        }
48    }
49}
50
51#[derive(Debug, Clone)]
52struct CompiledBackground {
53    node_id: NodeId,
54    params: BackgroundParamsDelegate,
55    buffer: lumen_gpu::BufferId,
56}
57
58impl GpuCompiledNode for CompiledBackground {
59    fn node_id(&self) -> NodeId {
60        self.node_id
61    }
62
63    fn bind(&self, ctx: &FrameBindContext<'_>, bound: &mut BoundFrame) -> crate::Result<()> {
64        let params = self.params.eval(&NodeParamEvalContext {
65            node_id: self.node_id,
66            expr: &ctx.expr_context(self.node_id, "params"),
67        })?;
68        bound.write_buffer(
69            self.buffer,
70            0,
71            bytemuck::bytes_of(&params.paint.to_gpu([0, 0, 0, 255])),
72        );
73        Ok(())
74    }
75}
76
77impl GpuCompileNode for Background {
78    fn compile_gpu(
79        &self,
80        ctx: &mut crate::gpu::CompileContext<'_>,
81        port: &crate::node::PortRef,
82    ) -> crate::Result<CompiledOutput> {
83        if port.port != "output" {
84            return Err(ctx.missing_output(self.id, &port.port));
85        }
86
87        let params = self.params.eval(&NodeParamEvalContext {
88            node_id: self.id,
89            expr: &ctx.expr_context(self.id, "params"),
90        })?;
91        let width = ctx.static_dimension_value(i64::from(params.width), "width");
92        let height = ctx.static_dimension_value(i64::from(params.height), "height");
93        let size = lumen_gpu::Size::new(width, height);
94        let texture = ctx.builder_mut().texture_for(
95            lumen_gpu::NodeKey(self.id.0),
96            Some(format!("background:{}:output", self.id.0)),
97            lumen_gpu::TextureDesc::storage(size, lumen_gpu::wgpu::TextureFormat::Rgba8Unorm),
98        );
99        let buffer = ctx.builder_mut().buffer_for(
100            lumen_gpu::NodeKey(self.id.0),
101            Some(format!("background:{}:params", self.id.0)),
102            lumen_gpu::BufferDesc::uniform(
103                std::mem::size_of::<crate::node::vector::paint::GpuPaint>() as u64,
104            ),
105        );
106        let program = ctx.builder_mut().program_for(
107            lumen_gpu::NodeKey(self.id.0),
108            lumen_gpu::ProgramDesc::Compute(lumen_gpu::ComputeProgramDesc {
109                label: Some("background".to_string()),
110                shader: SHADER.to_string(),
111                entry: "cs_main".to_string(),
112                bind_groups: lumen_gpu::BindGroupLayoutSpec::single(vec![
113                    lumen_gpu::BindingLayoutEntry::uniform(
114                        0,
115                        lumen_gpu::wgpu::ShaderStages::COMPUTE,
116                    ),
117                    lumen_gpu::BindingLayoutEntry::storage_texture(
118                        1,
119                        lumen_gpu::wgpu::ShaderStages::COMPUTE,
120                        lumen_gpu::wgpu::TextureFormat::Rgba8Unorm,
121                        lumen_gpu::wgpu::StorageTextureAccess::WriteOnly,
122                    ),
123                ]),
124            }),
125        );
126        ctx.builder_mut().compute_pass(lumen_gpu::ComputePassDesc {
127            label: Some(format!("background:{}:fill", self.id.0)),
128            owner: Some(lumen_gpu::NodeKey(self.id.0)),
129            program,
130            bindings: vec![
131                lumen_gpu::Binding::uniform(0, 0, buffer),
132                lumen_gpu::Binding::storage_texture(0, 1, texture),
133            ],
134            dispatch: compiler::dispatch_for(size).into(),
135        });
136        ctx.builder_mut().param(
137            lumen_gpu::ParamKey {
138                owner: lumen_gpu::NodeKey(self.id.0),
139                slot: 0,
140            },
141            lumen_gpu::ParamTarget::Buffer(buffer),
142        );
143        ctx.register_compiled_node(CompiledBackground {
144            node_id: self.id,
145            params: self.params.clone(),
146            buffer,
147        });
148
149        Ok(CompiledOutput::Raster(RasterHandle {
150            texture,
151            domain: lumen_gpu::TextureDomain::full_frame(size),
152            metadata: RasterMetadata::default(),
153        }))
154    }
155}