mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Move the partial rendering state into core
... and use from skia as well as the software renderer.
This commit is contained in:
parent
d706f81773
commit
a7e04b7d59
3 changed files with 145 additions and 172 deletions
|
@ -21,7 +21,9 @@ use crate::graphics::rendering_metrics_collector::{RefreshMode, RenderingMetrics
|
|||
use crate::graphics::{
|
||||
BorderRadius, PixelFormat, Rgba8Pixel, SharedImageBuffer, SharedPixelBuffer,
|
||||
};
|
||||
use crate::item_rendering::{CachedRenderingData, DirtyRegion, RenderBorderRectangle, RenderImage};
|
||||
use crate::item_rendering::{
|
||||
CachedRenderingData, DirtyRegion, PartialRenderingState, RenderBorderRectangle, RenderImage,
|
||||
};
|
||||
use crate::items::{ItemRc, TextOverflow, TextWrap};
|
||||
use crate::lengths::{
|
||||
LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector,
|
||||
|
@ -50,24 +52,7 @@ type PhysicalSize = euclid::Size2D<i16, PhysicalPx>;
|
|||
type PhysicalPoint = euclid::Point2D<i16, PhysicalPx>;
|
||||
type PhysicalBorderRadius = BorderRadius<i16, PhysicalPx>;
|
||||
|
||||
/// This enum describes which parts of the buffer passed to the [`SoftwareRenderer`] may be re-used to speed up painting.
|
||||
// FIXME: #[non_exhaustive] #3023
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Default, Copy)]
|
||||
pub enum RepaintBufferType {
|
||||
#[default]
|
||||
/// The full window is always redrawn. No attempt at partial rendering will be made.
|
||||
NewBuffer,
|
||||
/// Only redraw the parts that have changed since the previous call to render().
|
||||
///
|
||||
/// This variant assumes that the same buffer is passed on every call to render() and
|
||||
/// that it still contains the previously rendered frame.
|
||||
ReusedBuffer,
|
||||
|
||||
/// Redraw the part that have changed since the last two frames were drawn.
|
||||
///
|
||||
/// This is used when using double buffering and swapping of the buffers.
|
||||
SwappedBuffers,
|
||||
}
|
||||
pub use crate::item_rendering::RepaintBufferType;
|
||||
|
||||
/// This enum describes the rotation that should be applied to the contents rendered by the software renderer.
|
||||
///
|
||||
|
@ -392,15 +377,7 @@ fn region_line_ranges(
|
|||
/// is only useful if the device does not have enough memory to render the whole window
|
||||
/// in one single buffer
|
||||
pub struct SoftwareRenderer {
|
||||
partial_cache: RefCell<crate::item_rendering::PartialRenderingCache>,
|
||||
repaint_buffer_type: Cell<RepaintBufferType>,
|
||||
/// This is the area which we are going to redraw in the next frame, no matter if the items are dirty or not
|
||||
force_dirty: RefCell<DirtyRegion>,
|
||||
/// Force a redraw in the next frame, no matter what's dirty. Use only as a last resort.
|
||||
force_screen_refresh: Cell<bool>,
|
||||
/// This is the area which was dirty on the previous frame.
|
||||
/// Only used if repaint_buffer_type == RepaintBufferType::SwappedBuffers
|
||||
prev_frame_dirty: Cell<DirtyRegion>,
|
||||
partial_rendering_state: PartialRenderingState,
|
||||
maybe_window_adapter: RefCell<Option<Weak<dyn crate::window::WindowAdapter>>>,
|
||||
rotation: Cell<RenderingRotation>,
|
||||
rendering_metrics_collector: Option<Rc<RenderingMetricsCollector>>,
|
||||
|
@ -409,11 +386,7 @@ pub struct SoftwareRenderer {
|
|||
impl Default for SoftwareRenderer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
partial_cache: Default::default(),
|
||||
repaint_buffer_type: Default::default(),
|
||||
force_dirty: Default::default(),
|
||||
force_screen_refresh: Default::default(),
|
||||
prev_frame_dirty: Default::default(),
|
||||
partial_rendering_state: Default::default(),
|
||||
maybe_window_adapter: Default::default(),
|
||||
rotation: Default::default(),
|
||||
rendering_metrics_collector: RenderingMetricsCollector::new("software"),
|
||||
|
@ -431,21 +404,21 @@ impl SoftwareRenderer {
|
|||
///
|
||||
/// The `repaint_buffer_type` parameter specify what kind of buffer are passed to [`Self::render`]
|
||||
pub fn new_with_repaint_buffer_type(repaint_buffer_type: RepaintBufferType) -> Self {
|
||||
Self { repaint_buffer_type: repaint_buffer_type.into(), ..Default::default() }
|
||||
let self_ = Self::default();
|
||||
self_.partial_rendering_state.set_repaint_buffer_type(repaint_buffer_type);
|
||||
self_
|
||||
}
|
||||
|
||||
/// Change the what kind of buffer is being passed to [`Self::render`]
|
||||
///
|
||||
/// This may clear the internal caches
|
||||
pub fn set_repaint_buffer_type(&self, repaint_buffer_type: RepaintBufferType) {
|
||||
if self.repaint_buffer_type.replace(repaint_buffer_type) != repaint_buffer_type {
|
||||
self.partial_cache.borrow_mut().clear();
|
||||
}
|
||||
self.partial_rendering_state.set_repaint_buffer_type(repaint_buffer_type);
|
||||
}
|
||||
|
||||
/// Returns the kind of buffer that must be passed to [`Self::render`]
|
||||
pub fn repaint_buffer_type(&self) -> RepaintBufferType {
|
||||
self.repaint_buffer_type.get()
|
||||
self.partial_rendering_state.repaint_buffer_type()
|
||||
}
|
||||
|
||||
/// Set how the window need to be rotated in the buffer.
|
||||
|
@ -460,25 +433,6 @@ impl SoftwareRenderer {
|
|||
self.rotation.get()
|
||||
}
|
||||
|
||||
/// Internal function to apply a dirty region depending on the dirty_tracking_policy.
|
||||
/// Returns the region to actually draw.
|
||||
fn apply_dirty_region(&self, dirty_region: &mut DirtyRegion, screen_size: LogicalSize) {
|
||||
let screen_region = LogicalRect::from_size(screen_size);
|
||||
|
||||
if self.force_screen_refresh.take() {
|
||||
*dirty_region = screen_region.into();
|
||||
}
|
||||
|
||||
*dirty_region = match self.repaint_buffer_type() {
|
||||
RepaintBufferType::NewBuffer => screen_region.into(),
|
||||
RepaintBufferType::ReusedBuffer => dirty_region.clone(),
|
||||
RepaintBufferType::SwappedBuffers => {
|
||||
dirty_region.union(&self.prev_frame_dirty.replace(dirty_region.clone()))
|
||||
}
|
||||
}
|
||||
.intersection(screen_region)
|
||||
}
|
||||
|
||||
/// Render the window to the given frame buffer.
|
||||
///
|
||||
/// The renderer uses a cache internally and will only render the part of the window
|
||||
|
@ -538,19 +492,16 @@ impl SoftwareRenderer {
|
|||
},
|
||||
rotation,
|
||||
);
|
||||
let mut renderer = crate::item_rendering::PartialRenderer::new(
|
||||
&self.partial_cache,
|
||||
self.force_dirty.take(),
|
||||
buffer_renderer,
|
||||
);
|
||||
let mut renderer = self.partial_rendering_state.create_partial_renderer(buffer_renderer);
|
||||
|
||||
window_inner
|
||||
.draw_contents(|components| {
|
||||
let logical_size = (size.cast() / factor).cast();
|
||||
for (component, origin) in components {
|
||||
renderer.compute_dirty_regions(component, *origin, logical_size);
|
||||
}
|
||||
self.apply_dirty_region(&mut renderer.dirty_region, logical_size);
|
||||
self.partial_rendering_state.apply_dirty_region(
|
||||
&mut renderer,
|
||||
components,
|
||||
logical_size,
|
||||
);
|
||||
let rotation = RotationInfo { orientation: rotation, screen_size: size };
|
||||
let mut i = renderer.dirty_region.iter().map(|r| {
|
||||
(r.cast() * factor).to_rect().round_out().cast().transformed(rotation)
|
||||
|
@ -593,7 +544,7 @@ impl SoftwareRenderer {
|
|||
if let Some(metrics) = &self.rendering_metrics_collector {
|
||||
metrics.measure_frame_rendered(&mut renderer);
|
||||
if metrics.refresh_mode() == RefreshMode::FullSpeed {
|
||||
self.force_screen_refresh.set(true);
|
||||
self.partial_rendering_state.force_screen_refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -814,17 +765,12 @@ impl RendererSealed for SoftwareRenderer {
|
|||
_component: crate::item_tree::ItemTreeRef,
|
||||
items: &mut dyn Iterator<Item = Pin<crate::items::ItemRef<'_>>>,
|
||||
) -> Result<(), crate::platform::PlatformError> {
|
||||
for item in items {
|
||||
item.cached_rendering_data_offset().release(&mut self.partial_cache.borrow_mut());
|
||||
}
|
||||
// We don't have a way to determine the screen region of the delete items, what's in the cache is relative. So
|
||||
// as a last resort, refresh everything.
|
||||
self.force_screen_refresh.set(true);
|
||||
self.partial_rendering_state.free_graphics_resources(items);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mark_dirty_region(&self, region: crate::item_rendering::DirtyRegion) {
|
||||
self.force_dirty.replace_with(|r| r.union(®ion));
|
||||
self.partial_rendering_state.mark_dirty_region(region);
|
||||
}
|
||||
|
||||
fn register_bitmap_font(&self, font_data: &'static crate::graphics::BitmapFont) {
|
||||
|
@ -853,7 +799,7 @@ impl RendererSealed for SoftwareRenderer {
|
|||
|
||||
fn set_window_adapter(&self, window_adapter: &Rc<dyn WindowAdapter>) {
|
||||
*self.maybe_window_adapter.borrow_mut() = Some(Rc::downgrade(window_adapter));
|
||||
self.partial_cache.borrow_mut().clear();
|
||||
self.partial_rendering_state.clear_cache();
|
||||
}
|
||||
|
||||
fn take_snapshot(&self) -> Result<SharedPixelBuffer<Rgba8Pixel>, PlatformError> {
|
||||
|
@ -1014,20 +960,17 @@ fn prepare_scene(
|
|||
PrepareScene::default(),
|
||||
software_renderer.rotation.get(),
|
||||
);
|
||||
let mut renderer = crate::item_rendering::PartialRenderer::new(
|
||||
&software_renderer.partial_cache,
|
||||
software_renderer.force_dirty.take(),
|
||||
prepare_scene,
|
||||
);
|
||||
let mut renderer =
|
||||
software_renderer.partial_rendering_state.create_partial_renderer(prepare_scene);
|
||||
|
||||
let mut dirty_region = PhysicalRegion::default();
|
||||
window.draw_contents(|components| {
|
||||
let logical_size = (size.cast() / factor).cast();
|
||||
for (component, origin) in components {
|
||||
renderer.compute_dirty_regions(component, *origin, logical_size);
|
||||
}
|
||||
|
||||
software_renderer.apply_dirty_region(&mut renderer.dirty_region, logical_size);
|
||||
software_renderer.partial_rendering_state.apply_dirty_region(
|
||||
&mut renderer,
|
||||
components,
|
||||
logical_size,
|
||||
);
|
||||
let rotation =
|
||||
RotationInfo { orientation: software_renderer.rotation.get(), screen_size: size };
|
||||
let mut i = renderer
|
||||
|
@ -1048,7 +991,7 @@ fn prepare_scene(
|
|||
if let Some(metrics) = &software_renderer.rendering_metrics_collector {
|
||||
metrics.measure_frame_rendered(&mut renderer);
|
||||
if metrics.refresh_mode() == RefreshMode::FullSpeed {
|
||||
software_renderer.force_screen_refresh.set(true);
|
||||
software_renderer.partial_rendering_state.force_screen_refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue