Skip to main content

lumen_engine/node/source/
solid_color.rs

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