forma/
buffer.rs

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