mirror of
https://github.com/slint-ui/slint.git
synced 2025-12-23 09:19:32 +00:00
swrenderer: add a renderer that operate dirrectly onh the frame buffer
This commit is contained in:
parent
c10173ff2d
commit
5ca8f08ea7
5 changed files with 179 additions and 33 deletions
|
|
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue