render: remove getters from Surface, reimplement buffer sharing with Send+Sync

This commit is contained in:
Victoria Fischer 2024-12-02 19:36:17 +01:00
parent f789f6ded9
commit 6cafdcfa45
5 changed files with 247 additions and 114 deletions

View File

@ -29,8 +29,6 @@ pio = ["esp-idf-svc/pio"]
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
alloc = ["esp-idf-svc/alloc"]
threads = []
[dependencies]
log = { version = "0.4", default-features = false }
esp-idf-svc = { version = "0.49", default-features = false }

View File

@ -2,88 +2,259 @@ use crate::geometry::*;
use crate::lib8::interpolate::Fract8Ops;
use crate::power::AsMilliwatts;
use crate::render::{PixelView, Sample, Shader, Surface, Surfaces, HardwarePixel};
use crate::task::Task;
use std::borrow::BorrowMut;
use std::fmt::Debug;
use std::rc::Rc;
use std::cell::RefCell;
use std::io;
use std::ops::IndexMut;
#[cfg(feature="threads")]
use std::sync::atomic::AtomicBool;
use std::sync::RwLock;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub struct ShaderBinding {
struct ShaderBinding {
shader: Option<Box<dyn Shader>>,
rect: Rectangle<Virtual>,
opacity: u8
}
#[derive(Clone)]
pub struct BoundSurface<T> {
pub binding: T
#[derive(Debug)]
struct SurfaceUpdate {
shader: Option<Option<Box<dyn Shader>>>,
rect: Option<Rectangle<Virtual>>,
opacity: Option<u8>,
slot: usize
}
impl Debug for BoundSurface<Rc<RefCell<ShaderBinding>>> {
impl SurfaceUpdate {
fn merge(&mut self, mut other: Self) {
if other.shader.is_some() {
self.shader = other.shader.take()
}
if other.rect.is_some() {
self.rect = other.rect.take()
}
if other.opacity.is_some() {
self.opacity = other.opacity.take()
}
}
}
impl Debug for Box<dyn Shader> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BoundSurface")
.field("shader", &self.binding.borrow().shader)
.field("opacity", &self.binding.borrow().opacity)
.finish()
f.debug_struct("Shader").finish()
}
}
pub type SimpleSurface = BoundSurface<Rc<RefCell<ShaderBinding>>>;
impl Default for BoundSurface<Rc<RefCell<ShaderBinding>>>{
impl Default for SurfaceUpdate {
fn default() -> Self {
Self {
binding: Rc::new(RefCell::new(ShaderBinding {
shader: None,
rect: Rectangle::everything(),
opacity: 255
})),
SurfaceUpdate {
shader: None,
rect: None,
opacity: None,
slot: usize::MAX
}
}
}
impl Surface for BoundSurface<Rc<RefCell<ShaderBinding>>> {
fn rect(&self) -> Rectangle<Virtual> {
self.binding.borrow().rect
}
fn with_shader<F: FnMut(&dyn Shader)>(&self, mut f: F) {
if let Some(ref shader) = self.binding.borrow().shader {
f(shader.as_ref());
}
}
fn set_shader(&mut self, shader: Box<dyn Shader>) {
self.binding.borrow_mut().shader = Some(shader);
}
#[derive(Debug)]
pub struct BufferedSurface {
updater: Arc<UpdateQueue>,
slot: usize
}
impl Surface for BufferedSurface {
fn clear_shader(&mut self) {
self.binding.borrow_mut().shader = None;
}
fn set_rect(&mut self, rect: &Rectangle<Virtual>) {
self.binding.borrow_mut().rect = *rect;
}
fn opacity(&self) -> u8 {
self.binding.borrow().opacity
self.updater.push(SurfaceUpdate {
shader: None,
slot: self.slot,
..Default::default()
});
}
fn set_opacity(&mut self, opacity: u8) {
self.binding.borrow_mut().opacity = opacity
self.updater.push(SurfaceUpdate {
opacity: Some(opacity),
slot: self.slot,
..Default::default()
});
}
fn set_rect(&mut self, rect: &Rectangle<Virtual>) {
self.updater.push(SurfaceUpdate {
rect: Some(rect.clone()),
slot: self.slot,
..Default::default()
});
}
fn set_shader(&mut self, shader: Box<dyn Shader>) {
self.updater.push(SurfaceUpdate {
shader: Some(Some(shader)),
slot: self.slot,
..Default::default()
});
}
}
#[cfg(feature="threads")]
pub type SharedSurface = BoundSurface<Arc<Mutex<ShaderBinding>>>;
#[derive(Debug)]
struct UpdateQueue {
pending: Mutex<Vec<SurfaceUpdate>>,
damaged: AtomicBool
}
#[cfg(feature="threads")]
impl Default for BoundSurface<Arc<Mutex<ShaderBinding>>> {
impl UpdateQueue {
fn new() -> Self {
UpdateQueue {
pending: Mutex::new(Vec::new()),
damaged: AtomicBool::new(false)
}
}
fn push(&self, update: SurfaceUpdate) {
let mut locked = self.pending.lock().unwrap();
let mut existing_slot = None;
for existing in locked.iter_mut() {
if existing.slot == update.slot {
existing_slot = Some(existing);
break
}
}
match existing_slot {
Some(tgt) => {
log::debug!("Updating existing shader update");
tgt.merge(update);
}
_ => {
log::debug!("Pushing new shader update");
locked.push(update);
self.damaged.store(true, std::sync::atomic::Ordering::Relaxed);
}
}
}
}
#[derive(Debug)]
pub struct ShaderChain {
bindings: Vec<ShaderBinding>,
updates: Arc<UpdateQueue>
}
impl ShaderChain {
pub fn new() -> Self {
ShaderChain {
bindings: Vec::new(),
updates: Arc::new(UpdateQueue::new())
}
}
pub fn is_dirty(&self) -> bool {
self.updates.damaged.load(std::sync::atomic::Ordering::Relaxed)
}
pub fn commit(&mut self) {
log::info!("commit");
let mut queue: Vec<SurfaceUpdate> = {
let mut updates = self.updates.pending.lock().unwrap();
std::mem::take(updates.as_mut())
};
log::info!("queue={:?}", queue);
for update in queue.iter_mut() {
let target_slot = &mut self.bindings[update.slot];
log::info!("orig={:?} update={:?}", target_slot, update);
if let Some(shader) = update.shader.take() {
target_slot.shader = shader;
}
if let Some(opacity) = update.opacity.take() {
target_slot.opacity = opacity;
}
if let Some(rect) = update.rect.take() {
target_slot.rect = rect;
}
}
self.updates.damaged.store(false, std::sync::atomic::Ordering::Relaxed);
}
}
impl Surfaces for ShaderChain {
type Error = ();
type Surface = BufferedSurface;
fn new_surface(&mut self, area: &Rectangle<Virtual>) -> Result<Self::Surface, Self::Error> {
let next_slot = self.bindings.len();
self.bindings.push(ShaderBinding {
opacity: 255,
shader: None,
rect: area.clone()
});
Ok(BufferedSurface {
updater: Arc::clone(&self.updates),
slot: next_slot
})
}
fn render_to<S: Sample>(&self, output: &mut S, frame: usize) {
for surface in self.bindings.iter() {
let opacity = surface.opacity;
if opacity > 0 {
let rect = surface.rect;
let mut sample = output.sample(&rect);
if let Some(ref shader) = surface.shader {
while let Some((virt_coords, pixel)) = sample.next() {
*pixel = pixel.blend8(shader.draw(&virt_coords, frame).into(), opacity);
}
}
}
}
}
}
#[derive(Clone)]
pub struct BufferedSurfacePool {
pool: Arc<RwLock<ShaderChain>>
}
impl BufferedSurfacePool {
pub fn new() -> Self {
BufferedSurfacePool {
pool: Arc::new(RwLock::new(ShaderChain::new()))
}
}
}
impl Surfaces for BufferedSurfacePool {
type Error = ();
type Surface = <ShaderChain as Surfaces>::Surface;
fn new_surface(&mut self, area: &crate::geometry::Rectangle<crate::geometry::Virtual>) -> Result<Self::Surface, Self::Error> {
self.pool.write().unwrap().new_surface(area)
}
fn render_to<S: crate::render::Sample>(&self, output: &mut S, frame: usize) {
self.pool.read().unwrap().render_to(output, frame);
}
}
impl Task for BufferedSurfacePool {
fn tick(&mut self) {
if self.pool.read().unwrap().is_dirty() {
self.pool.write().unwrap().commit();
}
}
}
#[derive(Clone)]
pub struct SharedSurface {
binding: Arc<Mutex<ShaderBinding>>
}
impl Default for SharedSurface {
fn default() -> Self {
Self {
binding: Arc::new(Mutex::new(ShaderBinding {
@ -95,19 +266,7 @@ impl Default for BoundSurface<Arc<Mutex<ShaderBinding>>> {
}
}
#[cfg(feature="threads")]
impl Surface for BoundSurface<Arc<Mutex<ShaderBinding>>> {
fn rect(&self) -> Rectangle<Virtual> {
let r = self.binding.lock().unwrap();
r.rect.clone()
}
fn with_shader<F: FnMut(&dyn Shader)>(&self, mut f: F) {
if let Some(ref shader) = self.binding.lock().unwrap().shader {
f(shader.as_ref());
}
}
impl Surface for SharedSurface {
fn set_shader(&mut self, shader: Box<dyn Shader>) {
self.binding.lock().unwrap().shader = Some(shader);
}
@ -120,68 +279,48 @@ impl Surface for BoundSurface<Arc<Mutex<ShaderBinding>>> {
self.binding.lock().unwrap().rect = rect.clone();
}
fn opacity(&self) -> u8 {
self.binding.lock().unwrap().opacity
}
fn set_opacity(&mut self, opacity: u8) {
self.binding.lock().unwrap().opacity = opacity
}
}
#[cfg(feature="threads")]
impl Debug for BoundSurface<Arc<Mutex<ShaderBinding>>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BoundSurface")
.field("shader", &self.binding.lock().unwrap().shader)
.finish()
}
}
#[derive(Clone)]
pub struct SurfacePool<S: Surface + Default> {
surfaces: Vec<S>
pub struct SurfacePool {
surfaces: Vec<SharedSurface>
}
impl<S: Surface + Debug> Debug for SurfacePool<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.surfaces.fmt(f)
}
}
impl<S: Surface + Default> SurfacePool<S> {
impl SurfacePool {
pub const fn new() -> Self {
Self {
surfaces: Vec::new()
}
}
pub fn iter(&self) -> std::slice::Iter<S> {
self.surfaces.iter()
}
}
impl<S: Surface + Default> Surfaces for SurfacePool<S> {
type Surface = S;
impl Surfaces for SurfacePool {
type Surface = SharedSurface;
type Error = io::Error;
fn new_surface(&mut self, area: &Rectangle<Virtual>) -> Result<S, Self::Error> {
let mut surface = S::default();
fn new_surface(&mut self, area: &Rectangle<Virtual>) -> Result<Self::Surface, Self::Error> {
let mut surface = SharedSurface::default();
surface.set_rect(area);
self.surfaces.push(surface.clone());
return Ok(surface);
}
fn render_to<Sampler: Sample>(&self, output: &mut Sampler, frame: usize) {
for surface in self.iter() {
let opacity = surface.opacity();
for surface in self.surfaces.iter() {
let binding = surface.binding.lock().unwrap();
let opacity = binding.opacity;
if opacity > 0 {
let rect = surface.rect();
let rect = binding.rect;
let mut sample = output.sample(&rect);
surface.with_shader(|shader| {
if let Some(ref shader) = binding.shader {
while let Some((virt_coords, pixel)) = sample.next() {
*pixel = pixel.blend8(shader.draw(&virt_coords, frame).into(), opacity);
}
})
}
}
}
}

View File

@ -19,6 +19,10 @@ fn main() {
log::info!("Board: {}", core::any::type_name_of_val(&board));
log::info!("Creating tasks");
let mut system = board.system_tasks();
log::info!("System scheduler: {}", core::any::type_name_of_val(&system));
let output = board.output();
log::info!("Output: {}", core::any::type_name_of_val(&output));

View File

@ -18,30 +18,25 @@ use rgb::Rgb;
use super::Board;
use crate::buffers::BufferedSurfacePool;
use crate::task::FixedSizeScheduler;
use crate::task::Task;
use crate::buffers::{Pixbuf, SurfacePool};
use crate::buffers::Pixbuf;
use crate::mappings::StrideMapping;
use crate::platform::smart_leds_lib::StrideOutput;
use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt;
use crate::time::Periodically;
#[cfg(feature="threads")]
use crate::buffers::SharedSurface as SurfaceType;
#[cfg(not(feature="threads"))]
use crate::buffers::SimpleSurface as SurfaceType;
pub struct Esp32Board<'a> {
pub struct Esp32Board {
output: Option<<Self as Board>::Output>,
surfaces: Option<SurfacePool<SurfaceType>>,
sys_loop: EspSystemEventLoop,
modem: Option<Modem>,
surfaces: BufferedSurfacePool
}
impl<'a> Board for Esp32Board<'a> {
type Output = StrideOutput<[Rgb<u8>; 310], FastWs2812Esp32Rmt<'a>>;
type Surfaces = SurfacePool<SurfaceType>;
impl Board for Esp32Board {
type Output = StrideOutput<[Rgb<u8>; 310], FastWs2812Esp32Rmt<'static>>;
type Surfaces = BufferedSurfacePool;
type Scheduler = FixedSizeScheduler<2>;
fn take() -> Self {
@ -88,10 +83,10 @@ impl<'a> Board for Esp32Board<'a> {
);
Esp32Board {
surfaces: Some(SurfacePool::new()),
output: Some(output),
modem: Some(peripherals.modem),
sys_loop: sys_loop.clone(),
surfaces: BufferedSurfacePool::new()
}
}
@ -100,13 +95,14 @@ impl<'a> Board for Esp32Board<'a> {
}
fn surfaces(&mut self) -> Self::Surfaces {
self.surfaces.take().unwrap()
self.surfaces.clone()
}
fn system_tasks(&mut self) -> Self::Scheduler {
let nvs = EspDefaultNvsPartition::take().unwrap();
FixedSizeScheduler::new([
Box::new(WifiTask::new(self.modem.take().unwrap(), self.sys_loop.clone(), &nvs)),
Box::new(self.surfaces.clone())
])
}
}

View File

@ -1,4 +1,3 @@
use std::io;
use rgb::Rgb;
use crate::geometry::*;
@ -35,13 +34,10 @@ pub trait Surfaces: Send + Sync {
}
pub trait Surface: Send + Sync {
fn with_shader<F: FnMut(&dyn Shader)>(&self, f: F);
fn set_shader(&mut self, shader: Box<dyn Shader>);
fn clear_shader(&mut self);
fn rect(&self) -> Rectangle<Virtual>;
fn set_rect(&mut self, rect: &Rectangle<Virtual>);
fn opacity(&self) -> u8;
fn set_opacity(&mut self, opacity: u8);
}