Skip to main content

lgui_core/core/scene/render/
scene.rs

1use super::{memory::estimate_scene_commands_bytes, primitive::*, projection::project_command, *};
2
3#[derive(Clone, Debug, Default)]
4pub struct Scene {
5    pub(super) commands: Arc<Vec<ScenePrimitive>>,
6}
7
8impl Scene {
9    pub fn new() -> Self {
10        Self {
11            commands: Arc::new(Vec::new()),
12        }
13    }
14
15    pub fn push(&mut self, command: ScenePrimitive) {
16        Arc::make_mut(&mut self.commands).push(command);
17    }
18
19    pub fn commands(&self) -> &[ScenePrimitive] {
20        self.commands.as_slice()
21    }
22
23    #[doc(hidden)]
24    pub fn shares_command_storage_with(&self, other: &Self) -> bool {
25        Arc::ptr_eq(&self.commands, &other.commands)
26    }
27
28    pub fn image_requests(&self) -> Vec<ImageRequest> {
29        let mut requests = Vec::new();
30        collect_image_requests(self.commands(), &mut requests);
31        requests
32    }
33
34    pub fn estimated_bytes(&self) -> usize {
35        estimate_scene_commands_bytes(self.commands())
36    }
37
38    pub(super) fn move_popup_commands_to_end(&mut self) {
39        let commands = Arc::make_mut(&mut self.commands);
40        let (popup, regular): (Vec<_>, Vec<_>) = std::mem::take(commands)
41            .into_iter()
42            .partition(|command| command.phase() == RenderPhase::Popup);
43        *commands = regular;
44        commands.extend(popup);
45    }
46
47    pub(crate) fn replace_range(
48        &mut self,
49        range: std::ops::Range<usize>,
50        commands: impl IntoIterator<Item = ScenePrimitive>,
51    ) {
52        Arc::make_mut(&mut self.commands).splice(range, commands);
53    }
54
55    pub(crate) fn replace_all(&mut self, commands: Vec<ScenePrimitive>) {
56        self.commands = Arc::new(commands);
57    }
58
59    pub(crate) fn patch_compositing_layer_spec(
60        &mut self,
61        id: &UiId,
62        spec: CompositingLayerSpec,
63    ) -> bool {
64        let commands = Arc::make_mut(&mut self.commands);
65        patch_compositing_layer_spec(commands.as_mut_slice(), id, spec)
66    }
67
68    pub fn bounds(&self) -> Option<UiRect> {
69        self.commands()
70            .iter()
71            .map(ScenePrimitive::rect)
72            .reduce(UiRect::union)
73    }
74
75    pub fn project_to_physical(&self, scale: UiScale) -> Self {
76        if scale.is_identity() {
77            return self.clone();
78        }
79        Self {
80            commands: Arc::new(
81                self.commands
82                    .iter()
83                    .map(|command| project_command(command, scale))
84                    .collect(),
85            ),
86        }
87    }
88}
89
90fn collect_image_requests(commands: &[ScenePrimitive], requests: &mut Vec<ImageRequest>) {
91    for command in commands {
92        match command {
93            ScenePrimitive::Image { request, .. } => requests.push(request.clone()),
94            ScenePrimitive::CompositingLayer { commands, .. }
95            | ScenePrimitive::StaticLayer { commands, .. }
96            | ScenePrimitive::ScrollRaster { commands, .. }
97            | ScenePrimitive::Clip { commands, .. }
98            | ScenePrimitive::ClipPath { commands, .. } => {
99                collect_image_requests(commands, requests);
100            }
101            _ => {}
102        }
103    }
104}
105
106pub(crate) fn patch_compositing_layer_spec(
107    commands: &mut [ScenePrimitive],
108    source: &UiId,
109    next_spec: CompositingLayerSpec,
110) -> bool {
111    for command in commands {
112        let nested = match command {
113            ScenePrimitive::CompositingLayer {
114                id, spec, commands, ..
115            } => {
116                if id == source {
117                    *spec = next_spec;
118                    return true;
119                }
120                Some(commands)
121            }
122            ScenePrimitive::StaticLayer { commands, .. }
123            | ScenePrimitive::ScrollRaster { commands, .. }
124            | ScenePrimitive::Clip { commands, .. }
125            | ScenePrimitive::ClipPath { commands, .. } => Some(commands),
126            _ => None,
127        };
128        if nested.is_some_and(|commands| patch_compositing_layer_spec(commands, source, next_spec))
129        {
130            return true;
131        }
132    }
133    false
134}