swrenderer: add a renderer that operate dirrectly onh the frame buffer

This commit is contained in:
Olivier Goffart 2022-06-14 16:00:05 +02:00 committed by Simon Hausmann
parent c10173ff2d
commit 5ca8f08ea7
5 changed files with 179 additions and 33 deletions

View file

@ -31,6 +31,11 @@ pub type TargetPixel = embedded_graphics::pixelcolor::Rgb565;
pub trait Devices {
fn screen_size(&self) -> PhysicalSize;
/// If the device supports it, return the target buffer where to draw the frame. Must be width * height large.
/// Also return the dirty area.
fn get_buffer(&mut self) -> Option<(&mut [TargetPixel], PhysicalRect)> {
None
}
/// Called before the frame is being drawn, with the dirty region. Return the actual dirty region
fn prepare_frame(&mut self, dirty_region: PhysicalRect) -> PhysicalRect {
dirty_region
@ -87,7 +92,7 @@ where
}
thread_local! { static DEVICES: RefCell<Option<Box<dyn Devices + 'static>>> = RefCell::new(None) }
thread_local! { static LINE_RENDERER: RefCell<crate::renderer::LineRenderer> = RefCell::new(Default::default()) }
thread_local! { static RENDERER: RefCell<crate::renderer::SoftwareRenderer> = RefCell::new(Default::default()) }
mod the_backend {
use super::*;
@ -133,7 +138,7 @@ mod the_backend {
_: i_slint_core::component::ComponentRef,
items: &mut dyn Iterator<Item = Pin<i_slint_core::items::ItemRef<'a>>>,
) {
super::LINE_RENDERER.with(|renderer| {
super::RENDERER.with(|renderer| {
renderer.borrow().free_graphics_resources(items);
});
}
@ -276,9 +281,35 @@ mod the_backend {
DEVICES.with(|devices| {
let mut devices = devices.borrow_mut();
let devices = devices.as_mut().unwrap();
let size = devices.screen_size().to_f32() / runtime_window.scale_factor();
let mut frame_profiler = profiler::Timer::new(&**devices);
let screen_size = devices.screen_size();
let scale_factor = runtime_window.scale_factor();
let size = screen_size.to_f32() / scale_factor;
runtime_window.set_window_item_geometry(size.width as _, size.height as _);
if let Some((buffer, prev_dirty)) = devices.get_buffer() {
let init_dirty = PhysicalRect::from_untyped(
&window
.initial_dirty_region_for_next_frame
.take()
.to_rect()
.scale(scale_factor, scale_factor)
.cast(),
);
let new_dirty_region = RENDERER.with(|renderer| {
renderer.borrow().render(
runtime_window,
init_dirty.union(&prev_dirty),
buffer,
screen_size.width_length(),
)
});
devices.prepare_frame(new_dirty_region.union(&init_dirty));
devices.flush_frame();
frame_profiler.stop_profiling(&mut **devices, "=> frame total");
return;
}
struct BufferProvider<'a> {
screen_fill_profiler: profiler::Timer,
span_drawing_profiler: profiler::Timer,
@ -348,13 +379,14 @@ mod the_backend {
dirty_region: PhysicalRect::default(),
};
LINE_RENDERER.with(|renderer| {
renderer.borrow().render(
RENDERER.with(|renderer| {
renderer.borrow().render_by_line(
runtime_window,
window.initial_dirty_region_for_next_frame.take(),
buffer_provider,
)
});
frame_profiler.stop_profiling(&mut **devices, "=> frame total");
});
}
}

View file

@ -142,7 +142,7 @@ impl PlatformWindow for SimulatorWindow {
_: i_slint_core::component::ComponentRef,
items: &mut dyn Iterator<Item = std::pin::Pin<i_slint_core::items::ItemRef<'a>>>,
) {
super::LINE_RENDERER.with(|cache| {
super::RENDERER.with(|cache| {
cache.borrow().free_graphics_resources(items);
});
}
@ -298,7 +298,7 @@ impl WinitWindow for SimulatorWindow {
width: size.width,
height: size.height,
}));
super::LINE_RENDERER.with(|cache| {
super::RENDERER.with(|cache| {
*cache.borrow_mut() = Default::default();
});
buffer
@ -324,8 +324,8 @@ impl WinitWindow for SimulatorWindow {
);
}
}
super::LINE_RENDERER.with(|renderer| {
renderer.borrow().render(
super::RENDERER.with(|renderer| {
renderer.borrow().render_by_line(
runtime_window,
self.initial_dirty_region_for_next_frame.take(),
BufferProvider {

View file

@ -242,6 +242,10 @@ impl Devices for StmDevices {
PhysicalSize::new(DISPLAY_WIDTH as _, DISPLAY_HEIGHT as _)
}
fn get_buffer(&mut self) -> Option<(&mut [TargetPixel], PhysicalRect)> {
Some((self.work_fb, self.prev_dirty))
}
fn prepare_frame(&mut self, dirty_region: PhysicalRect) -> PhysicalRect {
dirty_region.union(&core::mem::replace(&mut self.prev_dirty, dirty_region))
}

View file

@ -41,11 +41,74 @@ pub trait LineBufferProvider {
}
#[derive(Default)]
pub struct LineRenderer {
pub struct SoftwareRenderer {
partial_cache: RefCell<crate::item_rendering::PartialRenderingCache>,
}
impl LineRenderer {
impl SoftwareRenderer {
/// Render the window to the given frame buffer.
///
/// returns the dirty region for this frame (not including the initial_dirty_region)
pub fn render(
&self,
window: Rc<crate::window::Window>,
initial_dirty_region: DirtyRegion,
buffer: &mut [impl TargetPixel],
buffer_stride: PhysicalLength,
) -> DirtyRegion {
let component_rc = window.component();
let component = crate::component::ComponentRc::borrow_pin(&component_rc);
let factor = ScaleFactor::new(window.scale_factor());
let size = if let Some(window_item) = crate::items::ItemRef::downcast_pin::<
crate::items::WindowItem,
>(component.as_ref().get_item_ref(0))
{
(euclid::size2(window_item.width() as f32, window_item.height() as f32) * factor).cast()
} else {
euclid::size2(buffer_stride.get(), (buffer.len() / (buffer_stride.get() as usize)) as _)
};
let buffer_renderer = SceneBuilder::new(
size,
factor,
window.default_font_properties(),
RenderToBuffer { buffer, stride: buffer_stride },
);
let mut renderer = crate::item_rendering::PartialRenderer::new(
&self.partial_cache,
Default::default(),
buffer_renderer,
);
let mut dirty_region = PhysicalRect::default();
window.draw_contents(|components| {
for (component, origin) in components {
renderer.compute_dirty_regions(component, *origin);
}
dirty_region = (LogicalRect::from_untyped(&renderer.dirty_region.to_rect()).cast()
* factor)
.round_out()
.cast();
renderer.combine_clip(
(dirty_region
.union(&initial_dirty_region)
.intersection(&PhysicalRect { origin: euclid::point2(0, 0), size })
.unwrap_or_default()
.cast()
/ factor)
.to_untyped()
.cast(),
0 as _,
0 as _,
);
for (component, origin) in components {
crate::item_rendering::render_component_items(component, &mut renderer, *origin);
}
});
dirty_region
}
/// Render the window, line by line, into the buffer provided by the `line_buffer` function.
///
/// The renderer uses a cache internally and will only render the part of the window
@ -56,7 +119,7 @@ impl LineRenderer {
/// TODO: what about async and threading.
/// (can we call the line_buffer function from different thread?)
/// TODO: should `initial_dirty_region` be set from a different call?
pub fn render(
pub fn render_by_line(
&self,
window: Rc<crate::window::Window>,
initial_dirty_region: crate::item_rendering::DirtyRegion,
@ -69,7 +132,7 @@ impl LineRenderer {
) {
let size = euclid::size2(window_item.width() as f32, window_item.height() as f32)
* ScaleFactor::new(window.scale_factor());
render_window_frame(
render_window_frame_by_line(
window,
window_item.background(),
size.cast(),
@ -92,7 +155,7 @@ impl LineRenderer {
}
}
fn render_window_frame(
fn render_window_frame_by_line(
runtime_window: Rc<crate::window::Window>,
background: Color,
size: PhysicalSize,
@ -124,7 +187,7 @@ fn render_window_frame(
SceneCommand::Texture { texture_index } => {
let texture = &scene.textures[texture_index as usize];
draw_functions::draw_texture_line(
span,
&PhysicalRect{ origin: span.pos, size: span.size } ,
scene.current_line,
texture,
line_buffer,
@ -133,7 +196,7 @@ fn render_window_frame(
SceneCommand::RoundedRectangle { rectangle_index } => {
let rr = &scene.rounded_rectangles[rectangle_index as usize];
draw_functions::draw_rounded_rectangle_line(
span,
&PhysicalRect{ origin: span.pos, size: span.size } ,
scene.current_line,
rr,
line_buffer,
@ -372,8 +435,12 @@ fn prepare_scene(
cache: &RefCell<PartialRenderingCache>,
) -> Scene {
let factor = ScaleFactor::new(runtime_window.scale_factor());
let prepare_scene =
SceneBuilder::<PrepareScene>::new(size, factor, runtime_window.default_font_properties());
let prepare_scene = SceneBuilder::new(
size,
factor,
runtime_window.default_font_properties(),
PrepareScene::default(),
);
let mut renderer =
crate::item_rendering::PartialRenderer::new(cache, initial_dirty_region, prepare_scene);
@ -412,6 +479,45 @@ trait ProcessScene {
fn process_rounded_rectangle(&mut self, geometry: PhysicalRect, data: RoundedRectangle);
}
struct RenderToBuffer<'a, TargetPixel> {
buffer: &'a mut [TargetPixel],
stride: PhysicalLength,
}
impl<'a, T: TargetPixel> ProcessScene for RenderToBuffer<'a, T> {
fn process_texture(&mut self, geometry: PhysicalRect, texture: SceneTexture) {
for line in geometry.min_y()..geometry.max_y() {
draw_functions::draw_texture_line(
&geometry,
PhysicalLength::new(line),
&texture,
&mut self.buffer[line as usize * self.stride.get() as usize..],
);
}
}
fn process_rectangle(&mut self, geometry: PhysicalRect, color: Color) {
for line in geometry.min_y()..geometry.max_y() {
let begin = line as usize * self.stride.get() as usize + geometry.origin.x as usize;
TargetPixel::blend_buffer(
&mut self.buffer[begin..begin + geometry.width() as usize],
color,
);
}
}
fn process_rounded_rectangle(&mut self, geometry: PhysicalRect, rr: RoundedRectangle) {
for line in geometry.min_y()..geometry.max_y() {
draw_functions::draw_rounded_rectangle_line(
&geometry,
PhysicalLength::new(line),
&rr,
&mut self.buffer[line as usize * self.stride.get() as usize..],
);
}
}
}
#[derive(Default)]
struct PrepareScene {
items: Vec<SceneItem>,
@ -466,10 +572,15 @@ struct SceneBuilder<T> {
default_font: FontRequest,
}
impl<T: ProcessScene + Default> SceneBuilder<T> {
fn new(size: PhysicalSize, scale_factor: ScaleFactor, default_font: FontRequest) -> Self {
impl<T: ProcessScene> SceneBuilder<T> {
fn new(
size: PhysicalSize,
scale_factor: ScaleFactor,
default_font: FontRequest,
processor: T,
) -> Self {
Self {
processor: Default::default(),
processor,
state_stack: vec![],
current_state: RenderState {
alpha: 1.,
@ -597,7 +708,7 @@ struct RenderState {
clip: LogicalRect,
}
impl<T: ProcessScene + Default + 'static> crate::item_rendering::ItemRenderer for SceneBuilder<T> {
impl<T: ProcessScene> crate::item_rendering::ItemRenderer for SceneBuilder<T> {
fn draw_rectangle(&mut self, rect: Pin<&crate::items::Rectangle>, _: &ItemRc) {
let geom = LogicalRect::new(LogicalPoint::default(), rect.logical_geometry().size_length());
if self.should_draw(&geom) {
@ -891,7 +1002,7 @@ impl<T: ProcessScene + Default + 'static> crate::item_rendering::ItemRenderer fo
}
fn as_any(&mut self) -> &mut dyn core::any::Any {
self
unimplemented!()
}
}

View file

@ -4,9 +4,8 @@
//! This is the module for the functions that are drawing the pixels
//! on the line buffer
use super::{SceneItem, SceneTexture};
use crate::graphics::PixelFormat;
use crate::lengths::{PhysicalLength, PointLengths, SizeLengths};
use crate::lengths::{PhysicalLength, PhysicalRect, PointLengths, SizeLengths};
use crate::Color;
use derive_more::{Add, Mul, Sub};
#[cfg(feature = "embedded-graphics")]
@ -15,19 +14,19 @@ use integer_sqrt::IntegerSquareRoot;
/// Draw one line of the texture in the line buffer
pub(super) fn draw_texture_line(
span: &SceneItem,
span: &PhysicalRect,
line: PhysicalLength,
texture: &super::SceneTexture,
line_buffer: &mut [impl TargetPixel],
) {
let SceneTexture { data, format, stride, source_size, color } = *texture;
let super::SceneTexture { data, format, stride, source_size, color } = *texture;
let source_size = source_size.cast::<usize>();
let span_size = span.size.cast::<usize>();
let bpp = super::bpp(format) as usize;
let y = (line - span.pos.y_length()).cast::<usize>();
let y = (line - span.origin.y_length()).cast::<usize>();
let y_pos = (y.get() * source_size.height / span_size.height) * stride as usize;
for (x, pix) in line_buffer
[span.pos.x as usize..(span.pos.x_length() + span.size.width_length()).get() as usize]
[span.origin.x as usize..(span.origin.x_length() + span.size.width_length()).get() as usize]
.iter_mut()
.enumerate()
{
@ -77,7 +76,7 @@ pub(super) fn draw_texture_line(
/// draw one line of the rounded rectangle in the line buffer
pub(super) fn draw_rounded_rectangle_line(
span: &SceneItem,
span: &PhysicalRect,
line: PhysicalLength,
rr: &super::RoundedRectangle,
line_buffer: &mut [impl TargetPixel],
@ -111,9 +110,9 @@ pub(super) fn draw_rounded_rectangle_line(
Self(self.0 * rhs.0)
}
}
let pos_x = span.pos.x as usize;
let y1 = (line - span.pos.y_length()) + rr.top_clip;
let y2 = (span.pos.y_length() + span.size.height_length() - line) + rr.bottom_clip
let pos_x = span.origin.x as usize;
let y1 = (line - span.origin.y_length()) + rr.top_clip;
let y2 = (span.origin.y_length() + span.size.height_length() - line) + rr.bottom_clip
- PhysicalLength::new(1);
let y = y1.min(y2);
debug_assert!(y.get() >= 0,);