blob: 0b31d303ea91cd33e339a98c512525da4f7e4c04 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub use surpass::layout;
use surpass::painter::{CachedTile, Color};
use layout::{Flusher, Layout};
use crate::small_bit_set::SmallBitSet;
/// A short-lived description of the buffer being rendered into for the current frame.
///
/// # Examples
///
/// ```
/// # use forma::buffer::{BufferBuilder, layout::LinearLayout};
/// let width = 100;
/// let height = 100;
/// let mut buffer = vec![0; 100 * 4 * 100];
///
/// let _buffer = BufferBuilder::new(
/// &mut buffer,
/// &mut LinearLayout::new(width, width * 4, height),
/// ).build();
/// ```
#[derive(Debug)]
pub struct Buffer<'b, 'l, L: Layout> {
pub(crate) buffer: &'b mut [u8],
pub(crate) layout: &'l mut L,
pub(crate) layer_cache: Option<BufferLayerCache>,
pub(crate) flusher: Option<Box<dyn Flusher>>,
}
/// A builder for the [`Buffer`].
///
/// # Examples
///
/// ```
/// # use forma::buffer::{BufferBuilder, layout::LinearLayout};
/// let width = 100;
/// let height = 100;
/// let mut buffer = vec![0; 100 * 4 * 100];
/// let mut layout = LinearLayout::new(width, width * 4, height);
/// let _buffer = BufferBuilder::new(&mut buffer, &mut layout).build();
/// ```
#[derive(Debug)]
pub struct BufferBuilder<'b, 'l, L: Layout> {
buffer: Buffer<'b, 'l, L>,
}
impl<'b, 'l, L: Layout> BufferBuilder<'b, 'l, L> {
#[inline]
pub fn new(buffer: &'b mut [u8], layout: &'l mut L) -> Self {
Self { buffer: Buffer { buffer, layout, layer_cache: None, flusher: None } }
}
#[inline]
pub fn layer_cache(mut self, layer_cache: BufferLayerCache) -> Self {
self.buffer.layer_cache = Some(layer_cache);
self
}
#[inline]
pub fn flusher(mut self, flusher: Box<dyn Flusher>) -> Self {
self.buffer.flusher = Some(flusher);
self
}
#[inline]
pub fn build(self) -> Buffer<'b, 'l, L> {
self.buffer
}
}
#[derive(Debug)]
struct IdDropper {
id: u8,
buffers_with_caches: Weak<RefCell<SmallBitSet>>,
}
impl Drop for IdDropper {
fn drop(&mut self) {
if let Some(buffers_with_caches) = Weak::upgrade(&self.buffers_with_caches) {
buffers_with_caches.borrow_mut().remove(self.id);
}
}
}
#[derive(Debug)]
pub struct CacheInner {
pub clear_color: Option<Color>,
pub tiles: Vec<CachedTile>,
pub width: Option<usize>,
pub height: Option<usize>,
_id_dropper: IdDropper,
}
/// A per-[`Buffer`] cache that enables forma to skip rendering to buffer
/// regions that have not changed.
///
/// # Examples
///
/// ```
/// # use forma::{
/// # buffer::{BufferBuilder, layout::LinearLayout},
/// # Color, Composition, CpuRenderer, RGBA,
/// # };
/// let mut buffer = vec![0; 4];
///
/// let mut composition = Composition::new();
/// let mut renderer = CpuRenderer::new();
/// let layer_cache = renderer.create_buffer_layer_cache().unwrap();
///
/// renderer.render(
/// &mut composition,
/// &mut BufferBuilder::new(&mut buffer, &mut LinearLayout::new(1, 1 * 4, 1))
/// .layer_cache(layer_cache.clone())
/// .build(),
/// RGBA,
/// Color { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
/// None,
/// );
///
/// // Rendered white on first frame.
/// assert_eq!(buffer, [255; 4]);
///
/// // Reset buffer manually.
/// buffer = vec![0; 4];
///
/// renderer.render(
/// &mut composition,
/// &mut BufferBuilder::new(&mut buffer, &mut LinearLayout::new(1, 1 * 4, 1))
/// .layer_cache(layer_cache.clone())
/// .build(),
/// RGBA,
/// Color { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
/// None,
/// );
///
/// // Skipped rendering on second frame since nothing changed.
/// assert_eq!(buffer, [0; 4]);
/// ```
#[derive(Clone, Debug)]
pub struct BufferLayerCache {
pub(crate) id: u8,
pub(crate) cache: Rc<RefCell<CacheInner>>,
}
impl BufferLayerCache {
pub(crate) fn new(id: u8, buffers_with_caches: Weak<RefCell<SmallBitSet>>) -> Self {
Self {
id,
cache: Rc::new(RefCell::new(CacheInner {
clear_color: None,
tiles: Vec::new(),
width: None,
height: None,
_id_dropper: IdDropper { id, buffers_with_caches },
})),
}
}
#[inline]
pub fn clear(&self) {
let mut cache = self.cache.borrow_mut();
cache.clear_color = None;
cache.tiles.fill(CachedTile::default());
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
fn new_cache(bit_set: &Rc<RefCell<SmallBitSet>>) -> BufferLayerCache {
bit_set
.borrow_mut()
.first_empty_slot()
.map(|id| BufferLayerCache::new(id, Rc::downgrade(bit_set)))
.unwrap()
}
#[test]
fn clone_and_drop() {
let bit_set = Rc::new(RefCell::new(SmallBitSet::default()));
let cache0 = new_cache(&bit_set);
let cache1 = new_cache(&bit_set);
let cache2 = new_cache(&bit_set);
assert!(bit_set.borrow().contains(&0));
assert!(bit_set.borrow().contains(&1));
assert!(bit_set.borrow().contains(&2));
mem::drop(cache0.clone());
mem::drop(cache1.clone());
mem::drop(cache2.clone());
assert!(bit_set.borrow().contains(&0));
assert!(bit_set.borrow().contains(&1));
assert!(bit_set.borrow().contains(&2));
mem::drop(cache1);
assert!(bit_set.borrow().contains(&0));
assert!(!bit_set.borrow().contains(&1));
assert!(bit_set.borrow().contains(&2));
let cache1 = new_cache(&bit_set);
assert!(bit_set.borrow().contains(&0));
assert!(bit_set.borrow().contains(&1));
assert!(bit_set.borrow().contains(&2));
mem::drop(cache0);
mem::drop(cache1);
mem::drop(cache2);
assert!(!bit_set.borrow().contains(&0));
assert!(!bit_set.borrow().contains(&1));
assert!(!bit_set.borrow().contains(&2));
}
}