lumen_engine/node/source/
solid_color.rs1use 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#[derive(Debug, Clone, lumen_macros::Node)]
12#[node(kind = "solid_color", name = "Solid Color", category = "source")]
13pub struct SolidColor {
14 pub id: NodeId,
15 #[property(kind = "color")]
17 pub color: NodeProperty,
18 #[property(kind = "int", min = 0, step = 1)]
20 pub width: NodeProperty,
21 #[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}