1use std::collections::HashMap;
2
3use crate::media::MediaFrame;
4use crate::node::{NodeId, PortRef};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum AlphaMode {
8 Premultiplied,
9 Unpremultiplied,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ColorSpace {
14 Srgb,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct RasterMetadata {
19 pub alpha_mode: AlphaMode,
20 pub color_space: ColorSpace,
21}
22
23impl Default for RasterMetadata {
24 fn default() -> Self {
25 Self {
26 alpha_mode: AlphaMode::Premultiplied,
27 color_space: ColorSpace::Srgb,
28 }
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct RasterHandle {
34 pub texture: lumen_gpu::TextureId,
35 pub domain: lumen_gpu::TextureDomain,
36 pub metadata: RasterMetadata,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum CompiledOutput {
41 Raster(RasterHandle),
42 Empty,
43}
44
45impl CompiledOutput {
46 pub fn into_raster(self, node_id: NodeId, _port: &str) -> crate::Result<RasterHandle> {
47 match self {
48 Self::Raster(raster) => Ok(raster),
49 Self::Empty => Err(crate::error::RenderError::InvalidNodeOutputType {
50 frame: 0,
51 node_id,
52 expected: "Raster",
53 actual: "Empty",
54 }
55 .into()),
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
61pub enum FrameBinding {
62 SolidColor {
63 node_id: NodeId,
64 color: crate::node::NodeProperty,
65 buffer: lumen_gpu::BufferId,
66 },
67 Shape {
68 node_id: NodeId,
69 geometry_kind: crate::node::NodeProperty,
70 width: crate::node::NodeProperty,
71 height: crate::node::NodeProperty,
72 border_radius: crate::node::NodeProperty,
73 position: crate::node::NodeProperty,
74 fill_enabled: crate::node::NodeProperty,
75 fill_color: crate::node::NodeProperty,
76 stroke_enabled: crate::node::NodeProperty,
77 stroke_color: crate::node::NodeProperty,
78 stroke_width: crate::node::NodeProperty,
79 buffer: lumen_gpu::BufferId,
80 },
81 Text {
82 node_id: NodeId,
83 content: crate::node::NodeProperty,
84 font_family: crate::node::NodeProperty,
85 font_size: crate::node::NodeProperty,
86 font_weight: crate::node::NodeProperty,
87 font_style: crate::node::NodeProperty,
88 max_width: crate::node::NodeProperty,
89 position: crate::node::NodeProperty,
90 color: crate::node::NodeProperty,
91 alignment_horizontal: crate::node::NodeProperty,
92 alignment_vertical: crate::node::NodeProperty,
93 atlas_texture: lumen_gpu::TextureId,
94 globals_buffer: lumen_gpu::BufferId,
95 instances_buffer: lumen_gpu::BufferId,
96 atlas_size: lumen_gpu::Size,
97 max_glyphs: usize,
98 size: lumen_gpu::Size,
99 },
100 Path {
101 node_id: NodeId,
102 data: crate::node::NodeProperty,
103 position: crate::node::NodeProperty,
104 fill_enabled: crate::node::NodeProperty,
105 fill_color: crate::node::NodeProperty,
106 stroke_enabled: crate::node::NodeProperty,
107 stroke_color: crate::node::NodeProperty,
108 stroke_width: crate::node::NodeProperty,
109 params_buffer: lumen_gpu::BufferId,
110 points_buffer: lumen_gpu::BufferId,
111 max_points: usize,
112 },
113 AlphaPremultiply {
114 node_id: NodeId,
115 mode: crate::node::NodeProperty,
116 buffer: lumen_gpu::BufferId,
117 },
118 ChannelShuffle {
119 node_id: NodeId,
120 red: crate::node::NodeProperty,
121 green: crate::node::NodeProperty,
122 blue: crate::node::NodeProperty,
123 alpha: crate::node::NodeProperty,
124 buffer: lumen_gpu::BufferId,
125 },
126 ColorGrade {
127 node_id: NodeId,
128 lut_source: crate::node::NodeProperty,
129 strength: crate::node::NodeProperty,
130 interpolation: crate::node::NodeProperty,
131 params_buffer: lumen_gpu::BufferId,
132 lut_buffer: lumen_gpu::BufferId,
133 },
134 Exposure {
135 node_id: NodeId,
136 exposure: crate::node::NodeProperty,
137 contrast: crate::node::NodeProperty,
138 offset: crate::node::NodeProperty,
139 buffer: lumen_gpu::BufferId,
140 },
141 HueSaturation {
142 node_id: NodeId,
143 hue_degrees: crate::node::NodeProperty,
144 saturation: crate::node::NodeProperty,
145 lightness: crate::node::NodeProperty,
146 buffer: lumen_gpu::BufferId,
147 },
148 Levels {
149 node_id: NodeId,
150 black_point: crate::node::NodeProperty,
151 white_point: crate::node::NodeProperty,
152 gamma: crate::node::NodeProperty,
153 output_black: crate::node::NodeProperty,
154 output_white: crate::node::NodeProperty,
155 buffer: lumen_gpu::BufferId,
156 },
157 Blur {
158 node_id: NodeId,
159 radius: crate::node::NodeProperty,
160 buffer: lumen_gpu::BufferId,
161 },
162 Curves {
163 node_id: NodeId,
164 curve_source: crate::node::NodeProperty,
165 strength: crate::node::NodeProperty,
166 params_buffer: lumen_gpu::BufferId,
167 curve_buffer: lumen_gpu::BufferId,
168 },
169 Shadow {
170 node_id: NodeId,
171 offset_x: crate::node::NodeProperty,
172 offset_y: crate::node::NodeProperty,
173 radius: crate::node::NodeProperty,
174 color: crate::node::NodeProperty,
175 opacity: crate::node::NodeProperty,
176 buffer: lumen_gpu::BufferId,
177 },
178 WgslShader {
179 node_id: NodeId,
180 shader: crate::node::NodeProperty,
181 bindings: crate::node::NodeProperty,
182 buffer: lumen_gpu::BufferId,
183 },
184 Boolean {
185 node_id: NodeId,
186 operation: crate::node::NodeProperty,
187 threshold: crate::node::NodeProperty,
188 buffer: lumen_gpu::BufferId,
189 },
190 RasterMultiMerge {
191 node_id: NodeId,
192 opacity: crate::node::NodeProperty,
193 blend_mode: crate::node::NodeProperty,
194 buffer: lumen_gpu::BufferId,
195 },
196 Merge {
197 node_id: NodeId,
198 opacity: crate::node::NodeProperty,
199 blend_mode: crate::node::NodeProperty,
200 has_mask: bool,
201 buffer: lumen_gpu::BufferId,
202 },
203 Memo {
204 node_id: NodeId,
205 cache_id: crate::node::NodeProperty,
206 allow_expressions: crate::node::NodeProperty,
207 },
208 TimeRemap {
209 node_id: NodeId,
210 frame: crate::node::NodeProperty,
211 loop_enabled: crate::node::NodeProperty,
212 loop_start: crate::node::NodeProperty,
213 loop_end: crate::node::NodeProperty,
214 },
215 Transform {
216 node_id: NodeId,
217 scale_x: crate::node::NodeProperty,
218 scale_y: crate::node::NodeProperty,
219 translate_x: crate::node::NodeProperty,
220 translate_y: crate::node::NodeProperty,
221 rotate: crate::node::NodeProperty,
222 pivot_x: crate::node::NodeProperty,
223 pivot_y: crate::node::NodeProperty,
224 sampling: crate::node::NodeProperty,
225 buffer: lumen_gpu::BufferId,
226 },
227 Crop {
228 node_id: NodeId,
229 x: crate::node::NodeProperty,
230 y: crate::node::NodeProperty,
231 width: crate::node::NodeProperty,
232 height: crate::node::NodeProperty,
233 buffer: lumen_gpu::BufferId,
234 },
235 Resize {
236 node_id: NodeId,
237 width: crate::node::NodeProperty,
238 height: crate::node::NodeProperty,
239 mode: crate::node::NodeProperty,
240 sampling: crate::node::NodeProperty,
241 buffer: lumen_gpu::BufferId,
242 },
243 Switch {
244 node_id: NodeId,
245 selected_layer: Option<usize>,
246 },
247 MediaInput {
248 node_id: NodeId,
249 source: crate::node::NodeProperty,
250 texture: lumen_gpu::TextureId,
251 size: lumen_gpu::Size,
252 },
253}
254
255impl FrameBinding {
256 pub fn node_id(&self) -> NodeId {
257 match self {
258 Self::SolidColor { node_id, .. }
259 | Self::Shape { node_id, .. }
260 | Self::Text { node_id, .. }
261 | Self::Path { node_id, .. }
262 | Self::AlphaPremultiply { node_id, .. }
263 | Self::ChannelShuffle { node_id, .. }
264 | Self::ColorGrade { node_id, .. }
265 | Self::Exposure { node_id, .. }
266 | Self::HueSaturation { node_id, .. }
267 | Self::Levels { node_id, .. }
268 | Self::Blur { node_id, .. }
269 | Self::Curves { node_id, .. }
270 | Self::Shadow { node_id, .. }
271 | Self::WgslShader { node_id, .. }
272 | Self::Boolean { node_id, .. }
273 | Self::RasterMultiMerge { node_id, .. }
274 | Self::Merge { node_id, .. }
275 | Self::Memo { node_id, .. }
276 | Self::TimeRemap { node_id, .. }
277 | Self::Transform { node_id, .. }
278 | Self::Crop { node_id, .. }
279 | Self::Resize { node_id, .. }
280 | Self::Switch { node_id, .. }
281 | Self::MediaInput { node_id, .. } => *node_id,
282 }
283 }
284}
285
286#[derive(Debug, Clone)]
287pub struct CompiledComposition {
288 pub plan: lumen_gpu::RenderPlan,
289 pub output: RasterHandle,
290 pub node_outputs: HashMap<PortRef, CompiledOutput>,
291 pub frame_bindings: Vec<FrameBinding>,
292 pub frame_binding_frames: Vec<Option<u32>>,
293}
294
295#[derive(Debug, Clone, Default)]
296pub struct BoundFrame {
297 buffer_uploads: Vec<(lumen_gpu::BufferId, u64, Vec<u8>)>,
298 texture_uploads: Vec<TextureUpload>,
299 media_textures: Vec<MediaTextureUpload>,
300}
301
302#[derive(Debug, Clone)]
303enum TextureUpload {
304 Rgba8 {
305 id: lumen_gpu::TextureId,
306 data: Vec<u8>,
307 bytes_per_row: u32,
308 rows_per_image: u32,
309 },
310 Rgba8Region {
311 id: lumen_gpu::TextureId,
312 data: Vec<u8>,
313 origin: [u32; 3],
314 size: lumen_gpu::Size,
315 bytes_per_row: u32,
316 rows_per_image: u32,
317 },
318 Rgba16Float {
319 id: lumen_gpu::TextureId,
320 data: Vec<u16>,
321 bytes_per_row: u32,
322 rows_per_image: u32,
323 },
324}
325
326#[derive(Debug, Clone, PartialEq, Eq, Hash)]
327pub struct MediaTextureKey {
328 pub source: String,
329 pub frame: Option<u32>,
330 pub width: u32,
331 pub height: u32,
332}
333
334#[derive(Debug, Clone)]
335pub struct MediaTextureUpload {
336 pub texture: lumen_gpu::TextureId,
337 pub key: MediaTextureKey,
338 pub frame: MediaFrame,
339 pub size: lumen_gpu::Size,
340}
341
342impl BoundFrame {
343 pub fn new() -> Self {
344 Self::default()
345 }
346
347 pub fn write_buffer(&mut self, id: lumen_gpu::BufferId, offset: u64, data: impl Into<Vec<u8>>) {
348 self.buffer_uploads.push((id, offset, data.into()));
349 }
350
351 pub fn write_texture_rgba8(
352 &mut self,
353 id: lumen_gpu::TextureId,
354 data: impl Into<Vec<u8>>,
355 bytes_per_row: u32,
356 rows_per_image: u32,
357 ) {
358 self.texture_uploads.push(TextureUpload::Rgba8 {
359 id,
360 data: data.into(),
361 bytes_per_row,
362 rows_per_image,
363 });
364 }
365
366 pub fn write_texture_rgba8_region(
367 &mut self,
368 id: lumen_gpu::TextureId,
369 data: impl Into<Vec<u8>>,
370 origin: [u32; 3],
371 size: lumen_gpu::Size,
372 bytes_per_row: u32,
373 rows_per_image: u32,
374 ) {
375 self.texture_uploads.push(TextureUpload::Rgba8Region {
376 id,
377 data: data.into(),
378 origin,
379 size,
380 bytes_per_row,
381 rows_per_image,
382 });
383 }
384
385 pub fn write_texture_rgba16_float(
386 &mut self,
387 id: lumen_gpu::TextureId,
388 data: impl Into<Vec<u16>>,
389 bytes_per_row: u32,
390 rows_per_image: u32,
391 ) {
392 self.texture_uploads.push(TextureUpload::Rgba16Float {
393 id,
394 data: data.into(),
395 bytes_per_row,
396 rows_per_image,
397 });
398 }
399
400 pub fn use_media_texture(
401 &mut self,
402 texture: lumen_gpu::TextureId,
403 key: MediaTextureKey,
404 frame: MediaFrame,
405 size: lumen_gpu::Size,
406 ) {
407 self.media_textures.push(MediaTextureUpload {
408 texture,
409 key,
410 frame,
411 size,
412 });
413 }
414
415 pub fn media_textures(&self) -> &[MediaTextureUpload] {
416 &self.media_textures
417 }
418
419 pub fn buffer_upload_count(&self) -> usize {
420 self.buffer_uploads.len()
421 }
422
423 pub fn texture_upload_count(&self) -> usize {
424 self.texture_uploads.len()
425 }
426
427 pub fn frame_update(&self) -> lumen_gpu::FrameUpdate<'_> {
428 let mut update = lumen_gpu::FrameUpdate::new();
429 for (id, offset, data) in &self.buffer_uploads {
430 update.write_buffer(*id, *offset, data);
431 }
432 for upload in &self.texture_uploads {
433 match upload {
434 TextureUpload::Rgba8 {
435 id,
436 data,
437 bytes_per_row,
438 rows_per_image,
439 } => {
440 update.write_texture_rgba8(*id, data, *bytes_per_row, *rows_per_image);
441 }
442 TextureUpload::Rgba8Region {
443 id,
444 data,
445 origin,
446 size,
447 bytes_per_row,
448 rows_per_image,
449 } => {
450 update.write_texture_rgba8_region(
451 *id,
452 data,
453 *origin,
454 *size,
455 *bytes_per_row,
456 *rows_per_image,
457 );
458 }
459 TextureUpload::Rgba16Float {
460 id,
461 data,
462 bytes_per_row,
463 rows_per_image,
464 } => {
465 update.write_texture_rgba16_float(*id, data, *bytes_per_row, *rows_per_image);
466 }
467 }
468 }
469 update
470 }
471}