lumen_engine/node/processing/
resize.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!("resize.wgsl");
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, lumen_macros::NodeEnum)]
11#[repr(i64)]
12pub enum ResizeMode {
13 Stretch = 0,
14 Fit = 1,
15 Fill = 2,
16}
17
18impl ResizeMode {
19 pub fn from_int(value: i64) -> Self {
20 match value {
21 1 => Self::Fit,
22 2 => Self::Fill,
23 _ => Self::Stretch,
24 }
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, lumen_macros::NodeEnum)]
29#[repr(i64)]
30pub enum ResizeSampling {
31 Nearest = 0,
32 Linear = 1,
33}
34
35impl ResizeSampling {
36 pub fn from_int(value: i64) -> Self {
37 if value == Self::Nearest as i64 {
38 Self::Nearest
39 } else {
40 Self::Linear
41 }
42 }
43}
44
45#[derive(Debug, Clone, lumen_macros::Node)]
47#[node(kind = "resize", name = "Resize", category = "processing")]
48pub struct Resize {
49 pub id: NodeId,
50 #[property(kind = "int", min = 1, step = 1)]
52 pub width: NodeProperty,
53 #[property(kind = "int", min = 1, step = 1)]
55 pub height: NodeProperty,
56 #[property(kind = "enum", enum_type = ResizeMode)]
58 pub mode: NodeProperty,
59 #[property(kind = "enum", enum_type = ResizeSampling)]
61 pub sampling: NodeProperty,
62 #[input()]
63 pub source: PortRef,
64}
65
66impl Default for Resize {
67 fn default() -> Self {
68 Self {
69 id: NodeId::new(0),
70 width: NodeProperty::Int(1),
71 height: NodeProperty::Int(1),
72 mode: NodeProperty::Int(ResizeMode::Stretch as i64),
73 sampling: NodeProperty::Int(ResizeSampling::Linear as i64),
74 source: PortRef::empty(),
75 }
76 }
77}
78
79impl GpuCompileNode for Resize {
80 fn compile_gpu(
81 &self,
82 ctx: &mut crate::gpu::CompileContext<'_>,
83 port: &PortRef,
84 ) -> crate::Result<CompiledOutput> {
85 if port.port != "output" {
86 return Err(ctx.missing_output(self.id, &port.port));
87 }
88
89 let source = ctx
90 .compile_port(&self.source)?
91 .into_raster(self.source.id, &self.source.port)?;
92 let size = lumen_gpu::Size::new(
93 ctx.static_dimension(&self.width, self.id, "width")?,
94 ctx.static_dimension(&self.height, self.id, "height")?,
95 );
96 let texture = ctx.builder_mut().texture_for(
97 lumen_gpu::NodeKey(self.id.0),
98 Some(format!("resize:{}:output", self.id.0)),
99 lumen_gpu::TextureDesc::storage(size, lumen_gpu::wgpu::TextureFormat::Rgba8Unorm),
100 );
101 let params = ctx.builder_mut().buffer_for(
102 lumen_gpu::NodeKey(self.id.0),
103 Some(format!("resize:{}:params", self.id.0)),
104 lumen_gpu::BufferDesc::uniform(std::mem::size_of::<compiler::ResizeParams>() as u64),
105 );
106 let program = ctx.spatial_program(self.id, "resize", SHADER);
107 ctx.builder_mut().compute_pass(lumen_gpu::ComputePassDesc {
108 label: Some(format!("resize:{}:apply", self.id.0)),
109 owner: Some(lumen_gpu::NodeKey(self.id.0)),
110 program,
111 bindings: compiler::spatial_bindings(source.texture, params, texture),
112 dispatch: compiler::dispatch_for(size).into(),
113 });
114 ctx.builder_mut().param(
115 lumen_gpu::ParamKey {
116 owner: lumen_gpu::NodeKey(self.id.0),
117 slot: 0,
118 },
119 lumen_gpu::ParamTarget::Buffer(params),
120 );
121 ctx.push_frame_binding(FrameBinding::Resize {
122 node_id: self.id,
123 width: self.width.clone(),
124 height: self.height.clone(),
125 mode: self.mode.clone(),
126 sampling: self.sampling.clone(),
127 buffer: params,
128 });
129
130 Ok(CompiledOutput::Raster(RasterHandle {
131 texture,
132 domain: lumen_gpu::TextureDomain::full_frame(size),
133 metadata: source.metadata,
134 }))
135 }
136}
137
138impl GpuFrameBindNode for Resize {
139 fn bind_gpu_frame(
140 &self,
141 ctx: &FrameBindContext<'_>,
142 binding: &FrameBinding,
143 bound: &mut BoundFrame,
144 ) -> crate::Result<()> {
145 let FrameBinding::Resize {
146 node_id,
147 width,
148 height,
149 mode,
150 sampling,
151 buffer,
152 } = binding
153 else {
154 return Ok(());
155 };
156 let params = compiler::ResizeParams {
157 size: [
158 width
159 .resolve_int(*node_id, "width", &ctx.expr_context(*node_id, "width"))?
160 .max(1) as u32,
161 height
162 .resolve_int(*node_id, "height", &ctx.expr_context(*node_id, "height"))?
163 .max(1) as u32,
164 ],
165 mode: ResizeMode::from_int(mode.resolve_int(
166 *node_id,
167 "mode",
168 &ctx.expr_context(*node_id, "mode"),
169 )?) as u32,
170 sampling: ResizeSampling::from_int(sampling.resolve_int(
171 *node_id,
172 "sampling",
173 &ctx.expr_context(*node_id, "sampling"),
174 )?) as u32,
175 };
176 bound.write_buffer(*buffer, 0, bytemuck::bytes_of(¶ms));
177 Ok(())
178 }
179}