forma_render/cpu/buffer/
mod.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    cell::RefCell,
17    rc::{Rc, Weak},
18};
19
20use crate::{styling::Color, utils::SmallBitSet};
21
22use super::painter::CachedTile;
23
24pub mod layout;
25
26use self::layout::{Flusher, Layout};
27
28/// A short-lived description of the buffer being rendered into for the current frame.
29///
30/// # Examples
31///
32/// ```
33/// # use forma_render::cpu::buffer::{BufferBuilder, layout::LinearLayout};
34/// let width = 100;
35/// let height = 100;
36/// let mut buffer = vec![0; 100 * 4 * 100];
37///
38/// let _buffer = BufferBuilder::new(
39///     &mut buffer,
40///     &mut LinearLayout::new(width, width * 4, height),
41/// ).build();
42/// ```
43#[derive(Debug)]
44pub struct Buffer<'b, 'l, L: Layout> {
45    pub(crate) buffer: &'b mut [u8],
46    pub(crate) layout: &'l mut L,
47    pub(crate) layer_cache: Option<BufferLayerCache>,
48    pub(crate) flusher: Option<Box<dyn Flusher>>,
49}
50
51/// A builder for the [`Buffer`].
52///
53/// # Examples
54///
55/// ```
56/// # use forma_render::cpu::buffer::{BufferBuilder, layout::LinearLayout};
57/// let width = 100;
58/// let height = 100;
59/// let mut buffer = vec![0; 100 * 4 * 100];
60/// let mut layout = LinearLayout::new(width, width * 4, height);
61/// let _buffer = BufferBuilder::new(&mut buffer, &mut layout).build();
62/// ```
63#[derive(Debug)]
64pub struct BufferBuilder<'b, 'l, L: Layout> {
65    buffer: Buffer<'b, 'l, L>,
66}
67
68impl<'b, 'l, L: Layout> BufferBuilder<'b, 'l, L> {
69    #[inline]
70    pub fn new(buffer: &'b mut [u8], layout: &'l mut L) -> Self {
71        Self {
72            buffer: Buffer {
73                buffer,
74                layout,
75                layer_cache: None,
76                flusher: None,
77            },
78        }
79    }
80
81    #[inline]
82    pub fn layer_cache(mut self, layer_cache: BufferLayerCache) -> Self {
83        self.buffer.layer_cache = Some(layer_cache);
84        self
85    }
86
87    #[inline]
88    pub fn flusher(mut self, flusher: Box<dyn Flusher>) -> Self {
89        self.buffer.flusher = Some(flusher);
90        self
91    }
92
93    #[inline]
94    pub fn build(self) -> Buffer<'b, 'l, L> {
95        self.buffer
96    }
97}
98
99#[derive(Debug)]
100struct IdDropper {
101    id: u8,
102    buffers_with_caches: Weak<RefCell<SmallBitSet>>,
103}
104
105impl Drop for IdDropper {
106    fn drop(&mut self) {
107        if let Some(buffers_with_caches) = Weak::upgrade(&self.buffers_with_caches) {
108            buffers_with_caches.borrow_mut().remove(self.id);
109        }
110    }
111}
112
113#[derive(Debug)]
114pub(crate) struct CacheInner {
115    pub clear_color: Option<Color>,
116    pub tiles: Vec<CachedTile>,
117    pub width: Option<usize>,
118    pub height: Option<usize>,
119    _id_dropper: IdDropper,
120}
121
122/// A per-[`Buffer`] cache that enables forma to skip rendering to buffer
123/// regions that have not changed.
124///
125/// # Examples
126///
127/// ```
128/// # use forma_render::{
129/// #     cpu::{buffer::{BufferBuilder, layout::LinearLayout}, Renderer, RGBA},
130/// #     styling::Color, Composition,
131/// # };
132/// let mut buffer = vec![0; 4];
133///
134/// let mut composition = Composition::new();
135/// let mut renderer = Renderer::new();
136/// let layer_cache = renderer.create_buffer_layer_cache().unwrap();
137///
138/// renderer.render(
139///     &mut composition,
140///     &mut BufferBuilder::new(&mut buffer, &mut LinearLayout::new(1, 1 * 4, 1))
141///         .layer_cache(layer_cache.clone())
142///         .build(),
143///     RGBA,
144///     Color { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
145///     None,
146/// );
147///
148/// // Rendered white on first frame.
149/// assert_eq!(buffer, [255; 4]);
150///
151/// // Reset buffer manually.
152/// buffer = vec![0; 4];
153///
154/// renderer.render(
155///     &mut composition,
156///     &mut BufferBuilder::new(&mut buffer, &mut LinearLayout::new(1, 1 * 4, 1))
157///         .layer_cache(layer_cache.clone())
158///         .build(),
159///     RGBA,
160///     Color { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
161///     None,
162/// );
163///
164/// // Skipped rendering on second frame since nothing changed.
165/// assert_eq!(buffer, [0; 4]);
166/// ```
167#[derive(Clone, Debug)]
168pub struct BufferLayerCache {
169    pub(crate) id: u8,
170    pub(crate) cache: Rc<RefCell<CacheInner>>,
171}
172
173impl BufferLayerCache {
174    pub(crate) fn new(id: u8, buffers_with_caches: Weak<RefCell<SmallBitSet>>) -> Self {
175        Self {
176            id,
177            cache: Rc::new(RefCell::new(CacheInner {
178                clear_color: None,
179                tiles: Vec::new(),
180                width: None,
181                height: None,
182                _id_dropper: IdDropper {
183                    id,
184                    buffers_with_caches,
185                },
186            })),
187        }
188    }
189
190    #[inline]
191    pub fn clear(&self) {
192        let mut cache = self.cache.borrow_mut();
193
194        cache.clear_color = None;
195        cache.tiles.fill(CachedTile::default());
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202
203    use std::mem;
204
205    fn new_cache(bit_set: &Rc<RefCell<SmallBitSet>>) -> BufferLayerCache {
206        bit_set
207            .borrow_mut()
208            .first_empty_slot()
209            .map(|id| BufferLayerCache::new(id, Rc::downgrade(bit_set)))
210            .unwrap()
211    }
212
213    #[test]
214    fn clone_and_drop() {
215        let bit_set = Rc::new(RefCell::new(SmallBitSet::default()));
216
217        let cache0 = new_cache(&bit_set);
218        let cache1 = new_cache(&bit_set);
219        let cache2 = new_cache(&bit_set);
220
221        assert!(bit_set.borrow().contains(&0));
222        assert!(bit_set.borrow().contains(&1));
223        assert!(bit_set.borrow().contains(&2));
224
225        mem::drop(cache0.clone());
226        mem::drop(cache1.clone());
227        mem::drop(cache2.clone());
228
229        assert!(bit_set.borrow().contains(&0));
230        assert!(bit_set.borrow().contains(&1));
231        assert!(bit_set.borrow().contains(&2));
232
233        mem::drop(cache1);
234
235        assert!(bit_set.borrow().contains(&0));
236        assert!(!bit_set.borrow().contains(&1));
237        assert!(bit_set.borrow().contains(&2));
238
239        let cache1 = new_cache(&bit_set);
240
241        assert!(bit_set.borrow().contains(&0));
242        assert!(bit_set.borrow().contains(&1));
243        assert!(bit_set.borrow().contains(&2));
244
245        mem::drop(cache0);
246        mem::drop(cache1);
247        mem::drop(cache2);
248
249        assert!(!bit_set.borrow().contains(&0));
250        assert!(!bit_set.borrow().contains(&1));
251        assert!(!bit_set.borrow().contains(&2));
252    }
253}