forma_render/cpu/
renderer.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    borrow::Cow,
17    cell::{RefCell, RefMut},
18    ops::Range,
19    rc::Rc,
20};
21
22use rustc_hash::FxHashMap;
23
24use crate::{
25    consts,
26    cpu::painter::{self, CachedTile, LayerProps},
27    styling::{Color, Props},
28    utils::{Order, SmallBitSet},
29    Composition, Layer,
30};
31
32use super::{
33    buffer::{layout::Layout, Buffer, BufferLayerCache},
34    Channel, Rasterizer,
35};
36
37#[derive(Clone, Debug, Eq, Hash, PartialEq)]
38pub struct Rect {
39    pub(crate) hor: Range<usize>,
40    pub(crate) vert: Range<usize>,
41}
42
43impl Rect {
44    /// The resulting rectangle is currently approximated to the tile grid.
45    pub fn new(horizontal: Range<usize>, vertical: Range<usize>) -> Self {
46        Self {
47            hor: horizontal.start / consts::cpu::TILE_WIDTH
48                ..(horizontal.end + consts::cpu::TILE_WIDTH - 1) / consts::cpu::TILE_WIDTH,
49            vert: vertical.start / consts::cpu::TILE_HEIGHT
50                ..(vertical.end + consts::cpu::TILE_HEIGHT - 1) / consts::cpu::TILE_HEIGHT,
51        }
52    }
53}
54
55#[derive(Debug, Default)]
56pub struct Renderer {
57    rasterizer: Rasterizer<{ consts::cpu::TILE_WIDTH }, { consts::cpu::TILE_HEIGHT }>,
58    buffers_with_caches: Rc<RefCell<SmallBitSet>>,
59}
60
61impl Renderer {
62    #[inline]
63    pub fn new() -> Self {
64        Self::default()
65    }
66
67    #[inline]
68    pub fn create_buffer_layer_cache(&mut self) -> Option<BufferLayerCache> {
69        self.buffers_with_caches
70            .borrow_mut()
71            .first_empty_slot()
72            .map(|id| BufferLayerCache::new(id, Rc::downgrade(&self.buffers_with_caches)))
73    }
74
75    pub fn render<L>(
76        &mut self,
77        composition: &mut Composition,
78        buffer: &mut Buffer<'_, '_, L>,
79        mut channels: [Channel; 4],
80        clear_color: Color,
81        crop: Option<Rect>,
82    ) where
83        L: Layout,
84    {
85        // If `clear_color` has alpha = 1 we can upgrade the alpha channel to `Channel::One`
86        // in order to skip reading the alpha channel.
87        if clear_color.a == 1.0 {
88            channels = channels.map(|c| match c {
89                Channel::Alpha => Channel::One,
90                c => c,
91            });
92        }
93
94        if let Some(layer_cache) = buffer.layer_cache.as_ref() {
95            let tiles_len = buffer.layout.width_in_tiles() * buffer.layout.height_in_tiles();
96            let cache = &layer_cache.cache;
97
98            cache
99                .borrow_mut()
100                .tiles
101                .resize(tiles_len, CachedTile::default());
102
103            if cache.borrow().width != Some(buffer.layout.width())
104                || cache.borrow().height != Some(buffer.layout.height())
105            {
106                cache.borrow_mut().width = Some(buffer.layout.width());
107                cache.borrow_mut().height = Some(buffer.layout.height());
108
109                layer_cache.clear();
110            }
111        }
112
113        composition.compact_geom();
114        composition
115            .shared_state
116            .borrow_mut()
117            .props_interner
118            .compact();
119
120        let layers = &composition.layers;
121        let shared_state = &mut *composition.shared_state.borrow_mut();
122        let segment_buffer = &mut shared_state.segment_buffer;
123        let geom_id_to_order = &shared_state.geom_id_to_order;
124        let rasterizer = &mut self.rasterizer;
125
126        struct CompositionContext<'l> {
127            layers: &'l FxHashMap<Order, Layer>,
128            cache_id: Option<u8>,
129        }
130
131        impl LayerProps for CompositionContext<'_> {
132            #[inline]
133            fn get(&self, id: u32) -> Cow<'_, Props> {
134                Cow::Borrowed(
135                    self.layers
136                        .get(&Order::new(id).expect("PixelSegment layer_id cannot overflow Order"))
137                        .map(|layer| &layer.props)
138                        .expect(
139                            "Layers outside of HashMap should not produce visible PixelSegments",
140                        ),
141                )
142            }
143
144            #[inline]
145            fn is_unchanged(&self, id: u32) -> bool {
146                match self.cache_id {
147                    None => false,
148                    Some(cache_id) => self
149                        .layers
150                        .get(&Order::new(id).expect("PixelSegment layer_id cannot overflow Order"))
151                        .map(|layer| layer.is_unchanged(cache_id))
152                        .expect(
153                            "Layers outside of HashMap should not produce visible PixelSegments",
154                        ),
155                }
156            }
157        }
158
159        let context = CompositionContext {
160            layers,
161            cache_id: buffer.layer_cache.as_ref().map(|cache| cache.id),
162        };
163
164        // `take()` sets the RefCell's content with `Default::default()` which is cheap for Option.
165        let builder = segment_buffer
166            .take()
167            .expect("segment_buffer should not be None");
168
169        *segment_buffer = {
170            let lines = {
171                duration!("gfx", "SegmentBuffer::fill_cpu_view");
172                builder.fill_cpu_view(|id| {
173                    geom_id_to_order
174                        .get(&id)
175                        .copied()
176                        .flatten()
177                        .and_then(|order| context.layers.get(&order))
178                        .map(|layer| layer.inner.clone())
179                })
180            };
181
182            {
183                duration!("gfx", "Rasterizer::rasterize");
184                rasterizer.rasterize(&lines);
185            }
186            {
187                duration!("gfx", "Rasterizer::sort");
188                rasterizer.sort();
189            }
190
191            let previous_clear_color = buffer
192                .layer_cache
193                .as_ref()
194                .and_then(|layer_cache| layer_cache.cache.borrow().clear_color);
195
196            let cached_tiles = buffer.layer_cache.as_ref().map(|layer_cache| {
197                RefMut::map(layer_cache.cache.borrow_mut(), |cache| &mut cache.tiles)
198            });
199
200            {
201                duration!("gfx", "painter::for_each_row");
202                painter::for_each_row(
203                    buffer.layout,
204                    buffer.buffer,
205                    channels,
206                    buffer.flusher.as_deref(),
207                    previous_clear_color,
208                    cached_tiles,
209                    rasterizer.segments(),
210                    clear_color,
211                    &crop,
212                    &context,
213                );
214            }
215
216            Some(lines.recycle())
217        };
218
219        if let Some(buffer_layer_cache) = &buffer.layer_cache {
220            buffer_layer_cache.cache.borrow_mut().clear_color = Some(clear_color);
221
222            for layer in composition.layers.values_mut() {
223                layer.set_is_unchanged(buffer_layer_cache.id, layer.inner.is_enabled);
224            }
225        }
226    }
227}