lumen_engine/node/processing/
levels.rs1use 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!("levels.wgsl");
9
10#[derive(Debug, Clone, lumen_macros::Node)]
12#[node(kind = "levels", name = "Levels", category = "processing")]
13pub struct Levels {
14 pub id: NodeId,
15 #[property(kind = "float", min = 0, max = 1, step = 0.01)]
17 pub black_point: NodeProperty,
18 #[property(kind = "float", min = 0, max = 1, step = 0.01)]
20 pub white_point: NodeProperty,
21 #[property(kind = "float", min = 0.01, step = 0.01)]
23 pub gamma: NodeProperty,
24 #[property(kind = "float", min = 0, max = 1, step = 0.01)]
26 pub output_black: NodeProperty,
27 #[property(kind = "float", min = 0, max = 1, step = 0.01)]
29 pub output_white: NodeProperty,
30 #[input()]
31 pub source: PortRef,
32}
33
34impl Default for Levels {
35 fn default() -> Self {
36 Self {
37 id: NodeId::new(0),
38 black_point: NodeProperty::Float(0.0),
39 white_point: NodeProperty::Float(1.0),
40 gamma: NodeProperty::Float(1.0),
41 output_black: NodeProperty::Float(0.0),
42 output_white: NodeProperty::Float(1.0),
43 source: PortRef::empty(),
44 }
45 }
46}
47
48impl GpuCompileNode for Levels {
49 fn compile_gpu(
50 &self,
51 ctx: &mut crate::gpu::CompileContext<'_>,
52 port: &PortRef,
53 ) -> crate::Result<CompiledOutput> {
54 let (source, texture, params) = ctx.compile_unary_filter(
55 self.id,
56 &self.source,
57 port,
58 "levels",
59 SHADER,
60 std::mem::size_of::<compiler::LevelsParams>() as u64,
61 )?;
62 ctx.push_frame_binding(FrameBinding::Levels {
63 node_id: self.id,
64 black_point: self.black_point.clone(),
65 white_point: self.white_point.clone(),
66 gamma: self.gamma.clone(),
67 output_black: self.output_black.clone(),
68 output_white: self.output_white.clone(),
69 buffer: params,
70 });
71 Ok(CompiledOutput::Raster(RasterHandle {
72 texture,
73 domain: source.domain,
74 metadata: source.metadata,
75 }))
76 }
77}
78
79impl GpuFrameBindNode for Levels {
80 fn bind_gpu_frame(
81 &self,
82 ctx: &FrameBindContext<'_>,
83 binding: &FrameBinding,
84 bound: &mut BoundFrame,
85 ) -> crate::Result<()> {
86 let FrameBinding::Levels {
87 node_id,
88 black_point,
89 white_point,
90 gamma,
91 output_black,
92 output_white,
93 buffer,
94 } = binding
95 else {
96 return Ok(());
97 };
98 let params = compiler::LevelsParams {
99 black_point: black_point.resolve_float(
100 *node_id,
101 "black_point",
102 &ctx.expr_context(*node_id, "black_point"),
103 )? as f32,
104 white_point: white_point.resolve_float(
105 *node_id,
106 "white_point",
107 &ctx.expr_context(*node_id, "white_point"),
108 )? as f32,
109 gamma: gamma.resolve_float(*node_id, "gamma", &ctx.expr_context(*node_id, "gamma"))?
110 as f32,
111 output_black: output_black.resolve_float(
112 *node_id,
113 "output_black",
114 &ctx.expr_context(*node_id, "output_black"),
115 )? as f32,
116 output_white: output_white.resolve_float(
117 *node_id,
118 "output_white",
119 &ctx.expr_context(*node_id, "output_white"),
120 )? as f32,
121 _pad: [0.0; 3],
122 };
123 bound.write_buffer(*buffer, 0, bytemuck::bytes_of(¶ms));
124 Ok(())
125 }
126}