forma/renderer/
cpu.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::borrow::Cow;
6use std::cell::{RefCell, RefMut};
7use std::rc::Rc;
8
9use rustc_hash::FxHashMap;
10use surpass::layout::Layout;
11use surpass::painter::{self, CachedTile, Channel, Color, LayerProps, Props, Rect};
12use surpass::rasterizer::Rasterizer;
13use surpass::{Order, TILE_HEIGHT, TILE_WIDTH};
14
15use crate::buffer::{Buffer, BufferLayerCache};
16use crate::{Composition, Layer};
17
18use crate::small_bit_set::SmallBitSet;
19
20#[derive(Debug, Default)]
21pub struct CpuRenderer {
22    rasterizer: Rasterizer<TILE_WIDTH, TILE_HEIGHT>,
23    buffers_with_caches: Rc<RefCell<SmallBitSet>>,
24}
25
26impl CpuRenderer {
27    #[inline]
28    pub fn new() -> Self {
29        Self::default()
30    }
31
32    #[inline]
33    pub fn create_buffer_layer_cache(&mut self) -> Option<BufferLayerCache> {
34        self.buffers_with_caches
35            .borrow_mut()
36            .first_empty_slot()
37            .map(|id| BufferLayerCache::new(id, Rc::downgrade(&self.buffers_with_caches)))
38    }
39
40    pub fn render<L>(
41        &mut self,
42        composition: &mut Composition,
43        buffer: &mut Buffer<'_, '_, L>,
44        mut channels: [Channel; 4],
45        clear_color: Color,
46        crop: Option<Rect>,
47    ) where
48        L: Layout,
49    {
50        // If `clear_color` has alpha = 1 we can upgrade the alpha channel to `Channel::One`
51        // in order to skip reading the alpha channel.
52        if clear_color.a == 1.0 {
53            channels = channels.map(|c| match c {
54                Channel::Alpha => Channel::One,
55                c => c,
56            });
57        }
58
59        if let Some(layer_cache) = buffer.layer_cache.as_ref() {
60            let tiles_len = buffer.layout.width_in_tiles() * buffer.layout.height_in_tiles();
61            let cache = &layer_cache.cache;
62
63            cache.borrow_mut().tiles.resize(tiles_len, CachedTile::default());
64
65            if cache.borrow().width != Some(buffer.layout.width())
66                || cache.borrow().height != Some(buffer.layout.height())
67            {
68                cache.borrow_mut().width = Some(buffer.layout.width());
69                cache.borrow_mut().height = Some(buffer.layout.height());
70
71                layer_cache.clear();
72            }
73        }
74
75        composition.compact_geom();
76        composition.shared_state.borrow_mut().props_interner.compact();
77
78        let layers = &composition.layers;
79        let shared_state = &mut *composition.shared_state.borrow_mut();
80        let lines_builder = &mut shared_state.lines_builder;
81        let geom_id_to_order = &shared_state.geom_id_to_order;
82        let rasterizer = &mut self.rasterizer;
83
84        struct CompositionContext<'l> {
85            layers: &'l FxHashMap<Order, Layer>,
86            cache_id: Option<u8>,
87        }
88
89        impl LayerProps for CompositionContext<'_> {
90            #[inline]
91            fn get(&self, id: u32) -> Cow<'_, Props> {
92                Cow::Borrowed(
93                    self.layers
94                        .get(&Order::new(id).expect("PixelSegment layer_id cannot overflow Order"))
95                        .map(|layer| &layer.props)
96                        .expect(
97                            "Layers outside of HashMap should not produce visible PixelSegments",
98                        ),
99                )
100            }
101
102            #[inline]
103            fn is_unchanged(&self, id: u32) -> bool {
104                match self.cache_id {
105                    None => false,
106                    Some(cache_id) => self
107                        .layers
108                        .get(&Order::new(id).expect("PixelSegment layer_id cannot overflow Order"))
109                        .map(|layer| layer.is_unchanged(cache_id))
110                        .expect(
111                            "Layers outside of HashMap should not produce visible PixelSegments",
112                        ),
113                }
114            }
115        }
116
117        let context = CompositionContext {
118            layers,
119            cache_id: buffer.layer_cache.as_ref().map(|cache| cache.id),
120        };
121
122        // `take()` sets the RefCell's content with `Default::default()` which is cheap for Option.
123        let builder = lines_builder.take().expect("lines_builder should not be None");
124
125        *lines_builder = {
126            let lines = {
127                duration!(c"gfx", c"LinesBuilder::build");
128                builder.build(|id| {
129                    geom_id_to_order
130                        .get(&id)
131                        .copied()
132                        .flatten()
133                        .and_then(|order| context.layers.get(&order))
134                        .map(|layer| layer.inner.clone())
135                })
136            };
137
138            {
139                duration!(c"gfx", c"Rasterizer::rasterize");
140                rasterizer.rasterize(&lines);
141            }
142            {
143                duration!(c"gfx", c"Rasterizer::sort");
144                rasterizer.sort();
145            }
146
147            let previous_clear_color = buffer
148                .layer_cache
149                .as_ref()
150                .and_then(|layer_cache| layer_cache.cache.borrow().clear_color);
151
152            let cached_tiles = buffer.layer_cache.as_ref().map(|layer_cache| {
153                RefMut::map(layer_cache.cache.borrow_mut(), |cache| &mut cache.tiles)
154            });
155
156            {
157                duration!(c"gfx", c"painter::for_each_row");
158                painter::for_each_row(
159                    buffer.layout,
160                    buffer.buffer,
161                    channels,
162                    buffer.flusher.as_deref(),
163                    previous_clear_color,
164                    cached_tiles,
165                    rasterizer.segments(),
166                    clear_color,
167                    &crop,
168                    &context,
169                );
170            }
171
172            Some(lines.unwrap())
173        };
174
175        if let Some(buffer_layer_cache) = &buffer.layer_cache {
176            buffer_layer_cache.cache.borrow_mut().clear_color = Some(clear_color);
177
178            for layer in composition.layers.values_mut() {
179                layer.set_is_unchanged(buffer_layer_cache.id, layer.inner.is_enabled);
180            }
181        }
182    }
183}