WIP: refactor rendering cache

This commit is contained in:
Olivier Goffart 2022-05-27 14:33:31 +02:00
parent 9beef43a9d
commit 5ab3cdce6d
16 changed files with 226 additions and 139 deletions

View file

@ -151,6 +151,7 @@ fn gen_corelib(
"PointerEventKind",
"PointerEventButton",
"PointerEvent",
"RenderingDataVTable",
]
.iter()
.chain(items.iter())
@ -199,6 +200,7 @@ fn gen_corelib(
"slint_image_size",
"slint_image_path",
"Coord",
"ComponentRenderingData",
]
.iter()
.chain(public_exported_types.iter())
@ -433,9 +435,10 @@ fn gen_corelib(
.with_after_include(
r"
namespace slint {
namespace private_api { class WindowRc; }
namespace private_api { class WindowRc; class ComponentRenderingData; }
namespace cbindgen_private {
using slint::private_api::WindowRc;
using slint::private_api::ComponentRenderingData;
using namespace vtable;
struct KeyEvent; struct PointerEvent;
using private_api::Property;

View file

@ -179,6 +179,10 @@ private:
cbindgen_private::WindowRcOpaque inner;
};
class ComponentRenderingData {
vtable::VBox<cbindgen_private::RenderingDataVTable> data;
};
constexpr inline ItemTreeNode make_item_node(uint32_t child_count, uint32_t child_index,
uint32_t parent_index, uint32_t item_array_index)
{

View file

@ -288,7 +288,7 @@ pub mod re_exports {
pub use i_slint_core::callbacks::Callback;
pub use i_slint_core::component::{
free_component_item_graphics_resources, init_component_items, Component, ComponentRefPin,
ComponentVTable, ComponentWeak, IndexRange,
ComponentRenderingData, ComponentVTable, ComponentWeak, IndexRange,
};
pub use i_slint_core::graphics::*;
pub use i_slint_core::input::{

View file

@ -10,7 +10,7 @@ use i_slint_core::graphics::rendering_metrics_collector::RenderingMetrics;
use i_slint_core::graphics::{Image, IntRect, Point, Rect, RenderingCache, Size};
use i_slint_core::item_rendering::{CachedRenderingData, ItemRenderer};
use i_slint_core::items::{
Clip, FillRule, ImageFit, ImageRendering, InputType, Item, ItemRc, Layer, Opacity,
self, Clip, FillRule, ImageFit, ImageRendering, InputType, Item, ItemRc, Layer, Opacity,
RenderingResult,
};
use i_slint_core::window::WindowRc;
@ -102,7 +102,7 @@ fn adjust_rect_and_border_for_inner_drawing(rect: &mut Rect, border_width: &mut
rect.size.height -= *border_width;
}
fn item_rect<Item: i_slint_core::items::Item>(item: Pin<&Item>, scale_factor: f32) -> Rect {
fn item_rect<Item: items::Item>(item: Pin<&Item>, scale_factor: f32) -> Rect {
let geometry = item.geometry();
euclid::rect(0., 0., geometry.width() * scale_factor, geometry.height() * scale_factor)
}
@ -147,7 +147,7 @@ fn clip_path_for_rect_alike_item(
}
impl ItemRenderer for GLItemRenderer {
fn draw_rectangle(&mut self, rect: std::pin::Pin<&i_slint_core::items::Rectangle>) {
fn draw_rectangle(&mut self, rect: Pin<&items::Rectangle>, _: &ItemRc) {
let geometry = item_rect(rect, self.scale_factor);
if geometry.is_empty() {
return;
@ -164,10 +164,7 @@ impl ItemRenderer for GLItemRenderer {
self.canvas.borrow_mut().fill_path(&mut path, paint)
}
fn draw_border_rectangle(
&mut self,
rect: std::pin::Pin<&i_slint_core::items::BorderRectangle>,
) {
fn draw_border_rectangle(&mut self, rect: Pin<&items::BorderRectangle>, _: &ItemRc) {
let mut geometry = item_rect(rect, self.scale_factor);
if geometry.is_empty() {
return;
@ -198,23 +195,20 @@ impl ItemRenderer for GLItemRenderer {
}
}
fn draw_image(&mut self, image: std::pin::Pin<&i_slint_core::items::ImageItem>) {
fn draw_image(&mut self, image: Pin<&items::ImageItem>, _: &ItemRc) {
self.draw_image_impl(
&image.cached_rendering_data,
i_slint_core::items::ImageItem::FIELD_OFFSETS.source.apply_pin(image),
items::ImageItem::FIELD_OFFSETS.source.apply_pin(image),
IntRect::default(),
i_slint_core::items::ImageItem::FIELD_OFFSETS.width.apply_pin(image),
i_slint_core::items::ImageItem::FIELD_OFFSETS.height.apply_pin(image),
items::ImageItem::FIELD_OFFSETS.width.apply_pin(image),
items::ImageItem::FIELD_OFFSETS.height.apply_pin(image),
image.image_fit(),
None,
image.image_rendering(),
);
}
fn draw_clipped_image(
&mut self,
clipped_image: std::pin::Pin<&i_slint_core::items::ClippedImage>,
) {
fn draw_clipped_image(&mut self, clipped_image: Pin<&items::ClippedImage>, _: &ItemRc) {
let source_clip_rect = IntRect::new(
[clipped_image.source_clip_x(), clipped_image.source_clip_y()].into(),
[clipped_image.source_clip_width(), clipped_image.source_clip_height()].into(),
@ -222,19 +216,17 @@ impl ItemRenderer for GLItemRenderer {
self.draw_image_impl(
&clipped_image.cached_rendering_data,
i_slint_core::items::ClippedImage::FIELD_OFFSETS.source.apply_pin(clipped_image),
items::ClippedImage::FIELD_OFFSETS.source.apply_pin(clipped_image),
source_clip_rect,
i_slint_core::items::ClippedImage::FIELD_OFFSETS.width.apply_pin(clipped_image),
i_slint_core::items::ClippedImage::FIELD_OFFSETS.height.apply_pin(clipped_image),
items::ClippedImage::FIELD_OFFSETS.width.apply_pin(clipped_image),
items::ClippedImage::FIELD_OFFSETS.height.apply_pin(clipped_image),
clipped_image.image_fit(),
Some(
i_slint_core::items::ClippedImage::FIELD_OFFSETS.colorize.apply_pin(clipped_image),
),
Some(items::ClippedImage::FIELD_OFFSETS.colorize.apply_pin(clipped_image)),
clipped_image.image_rendering(),
);
}
fn draw_text(&mut self, text: std::pin::Pin<&i_slint_core::items::Text>) {
fn draw_text(&mut self, text: Pin<&items::Text>, _: &ItemRc) {
let max_width = text.width() * self.scale_factor;
let max_height = text.height() * self.scale_factor;
@ -276,7 +268,7 @@ impl ItemRenderer for GLItemRenderer {
);
}
fn draw_text_input(&mut self, text_input: std::pin::Pin<&i_slint_core::items::TextInput>) {
fn draw_text_input(&mut self, text_input: Pin<&items::TextInput>, _: &ItemRc) {
let width = text_input.width() * self.scale_factor;
let height = text_input.height() * self.scale_factor;
if width <= 0. || height <= 0. {
@ -324,7 +316,7 @@ impl ItemRenderer for GLItemRenderer {
Size::new(width, height),
(text_input.horizontal_alignment(), text_input.vertical_alignment()),
text_input.wrap(),
i_slint_core::items::TextOverflow::clip,
items::TextOverflow::clip,
text_input.single_line(),
paint,
|to_draw, pos, start, metrics| {
@ -435,7 +427,7 @@ impl ItemRenderer for GLItemRenderer {
}
}
fn draw_path(&mut self, path: std::pin::Pin<&i_slint_core::items::Path>) {
fn draw_path(&mut self, path: Pin<&items::Path>, _: &ItemRc) {
let elements = path.elements();
if matches!(elements, i_slint_core::PathData::None) {
return;
@ -549,7 +541,7 @@ impl ItemRenderer for GLItemRenderer {
/// * Blur the image
/// * Fill the image with the shadow color and SourceIn as composition mode
/// * Draw the shadow image
fn draw_box_shadow(&mut self, box_shadow: std::pin::Pin<&i_slint_core::items::BoxShadow>) {
fn draw_box_shadow(&mut self, box_shadow: Pin<&items::BoxShadow>, item_rc: &ItemRc) {
if box_shadow.color().alpha() == 0
|| (box_shadow.blur() == 0.0
&& box_shadow.offset_x() == 0.
@ -1097,10 +1089,10 @@ impl GLItemRenderer {
fn draw_image_impl(
&mut self,
item_cache: &CachedRenderingData,
source_property: std::pin::Pin<&Property<Image>>,
source_property: Pin<&Property<Image>>,
source_clip_rect: IntRect,
target_width: std::pin::Pin<&Property<f32>>,
target_height: std::pin::Pin<&Property<f32>>,
target_width: Pin<&Property<f32>>,
target_height: Pin<&Property<f32>>,
image_fit: ImageFit,
colorize_property: Option<Pin<&Property<Brush>>>,
image_rendering: ImageRendering,

View file

@ -466,7 +466,7 @@ struct QtItemRenderer {
}
impl ItemRenderer for QtItemRenderer {
fn draw_rectangle(&mut self, rect_: Pin<&items::Rectangle>) {
fn draw_rectangle(&mut self, rect_: Pin<&items::Rectangle>, _: &ItemRc) {
let rect: qttypes::QRectF = get_geometry!(items::Rectangle, rect_);
let brush: qttypes::QBrush = into_qbrush(rect_.background(), rect.width, rect.height);
let painter: &mut QPainterPtr = &mut self.painter;
@ -475,7 +475,7 @@ impl ItemRenderer for QtItemRenderer {
}}
}
fn draw_border_rectangle(&mut self, rect: std::pin::Pin<&items::BorderRectangle>) {
fn draw_border_rectangle(&mut self, rect: std::pin::Pin<&items::BorderRectangle>, _: &ItemRc) {
Self::draw_rectangle_impl(
&mut self.painter,
get_geometry!(items::BorderRectangle, rect),
@ -486,7 +486,7 @@ impl ItemRenderer for QtItemRenderer {
);
}
fn draw_image(&mut self, image: Pin<&items::ImageItem>) {
fn draw_image(&mut self, image: Pin<&items::ImageItem>, _: &ItemRc) {
let dest_rect: qttypes::QRectF = get_geometry!(items::ImageItem, image);
self.draw_image_impl(
&image.cached_rendering_data,
@ -501,7 +501,7 @@ impl ItemRenderer for QtItemRenderer {
);
}
fn draw_clipped_image(&mut self, image: Pin<&items::ClippedImage>) {
fn draw_clipped_image(&mut self, image: Pin<&items::ClippedImage>, _: &ItemRc) {
let dest_rect: qttypes::QRectF = get_geometry!(items::ClippedImage, image);
let source_rect = qttypes::QRectF {
x: image.source_clip_x() as _,
@ -522,7 +522,7 @@ impl ItemRenderer for QtItemRenderer {
);
}
fn draw_text(&mut self, text: std::pin::Pin<&items::Text>) {
fn draw_text(&mut self, text: std::pin::Pin<&items::Text>, _: &ItemRc) {
let rect: qttypes::QRectF = get_geometry!(items::Text, text);
let fill_brush: qttypes::QBrush = into_qbrush(text.color(), rect.width, rect.height);
let mut string: qttypes::QString = text.text().as_str().into();
@ -601,7 +601,7 @@ impl ItemRenderer for QtItemRenderer {
}}
}
fn draw_text_input(&mut self, text_input: std::pin::Pin<&items::TextInput>) {
fn draw_text_input(&mut self, text_input: std::pin::Pin<&items::TextInput>, _: &ItemRc) {
let rect: qttypes::QRectF = get_geometry!(items::TextInput, text_input);
let fill_brush: qttypes::QBrush = into_qbrush(text_input.color(), rect.width, rect.height);
let selection_foreground_color: u32 =
@ -696,7 +696,7 @@ impl ItemRenderer for QtItemRenderer {
}}
}
fn draw_path(&mut self, path: Pin<&items::Path>) {
fn draw_path(&mut self, path: Pin<&items::Path>, _: &ItemRc) {
let elements = path.elements();
if matches!(elements, PathData::None) {
return;
@ -758,7 +758,7 @@ impl ItemRenderer for QtItemRenderer {
}}
}
fn draw_box_shadow(&mut self, box_shadow: Pin<&items::BoxShadow>) {
fn draw_box_shadow(&mut self, box_shadow: Pin<&items::BoxShadow>, _: &ItemRc) {
let cached_shadow_pixmap = box_shadow
.cached_rendering_data
.get_or_update(&self.cache, || {

View file

@ -1022,6 +1022,28 @@ fn generate_item_tree(
}),
));
target_struct.members.push((
Access::Private,
Declaration::Var(Var {
ty: "slint::cbindgen_private::ComponentRenderingData".into(),
name: "component_rendering_data".into(),
..Default::default()
}),
));
target_struct.members.push((
Access::Private,
Declaration::Function(Function {
name: "rendering_data".into(),
signature: "(slint::private_api::ComponentRef component) -> const slint::cbindgen_private::ComponentRenderingData*".into(),
is_static: true,
statements: Some(vec![
format!("return &reinterpret_cast<const {item_tree_class_name}*>(component.instance)->component_rendering_data;"),
]),
..Default::default()
}),
));
target_struct.members.push((
Access::Public,
Declaration::Var(Var {
@ -1035,7 +1057,7 @@ fn generate_item_tree(
ty: "const slint::private_api::ComponentVTable".to_owned(),
name: format!("{}::static_vtable", item_tree_class_name),
init: Some(format!(
"{{ visit_children, get_item_ref, get_subtree_range, get_subtree_component, get_item_tree, parent_node, subtree_index, layout_info, slint::private_api::drop_in_place<{}>, slint::private_api::dealloc }}",
"{{ visit_children, get_item_ref, get_subtree_range, get_subtree_component, get_item_tree, parent_node, subtree_index, layout_info, rendering_data, slint::private_api::drop_in_place<{}>, slint::private_api::dealloc }}",
item_tree_class_name)
),
..Default::default()

View file

@ -996,7 +996,7 @@ fn generate_item_tree(
&sub_tree.root,
root,
parent_ctx.clone(),
extra_fields,
quote!(rendering_data: slint::re_exports::ComponentRenderingData, #extra_fields),
index_property,
);
let inner_component_id = self::inner_component_id(&sub_tree.root);
@ -1185,6 +1185,10 @@ fn generate_item_tree(
fn layout_info(self: ::core::pin::Pin<&Self>, orientation: slint::re_exports::Orientation) -> slint::re_exports::LayoutInfo {
self.layout_info(orientation)
}
fn rendering_data(&self) -> &slint::re_exports::ComponentRenderingData {
&self.rendering_data
}
}

View file

@ -5,6 +5,7 @@
//! This module contains the basic datastructures that are exposed to the C API
use crate::item_rendering::PartialRenderingCache;
use crate::item_tree::{
ItemTreeNode, ItemVisitorVTable, ItemWeak, TraversalOrder, VisitChildrenResult,
};
@ -75,6 +76,9 @@ pub struct ComponentVTable {
pub layout_info:
extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>, Orientation) -> LayoutInfo,
/// Returns a reference to the ComponentRenderingData for this component
pub rendering_data: extern "C" fn(VRef<ComponentVTable>) -> &ComponentRenderingData,
/// in-place destructor (for VRc)
pub drop_in_place: unsafe fn(VRefMut<ComponentVTable>) -> vtable::Layout,
/// dealloc function (for VRc)
@ -114,6 +118,40 @@ pub fn free_component_item_graphics_resources<Base>(
window.free_graphics_resources(&mut item_array.iter().map(|item| item.apply_pin(base)));
}
/// This is a vtable for a type that's internal to the renderer
#[vtable]
#[repr(C)]
pub struct RenderingDataVTable {
/// return a PartialRenderingCache for this component
partial_rendering_cache:
extern "C" fn(VRefMut<'_, RenderingDataVTable>) -> &mut PartialRenderingCache,
/// destructor for the VBox
drop: extern "C" fn(VRefMut<'_, RenderingDataVTable>),
}
//pub use RenderingDataVTable_static;
/// This is the data needed for the rendering that must be present in the component
#[repr(transparent)]
#[derive(Default)]
pub struct ComponentRenderingData(core::cell::Cell<Option<VBox<RenderingDataVTable>>>);
impl ComponentRenderingData {
/// Get, update, and modify the component cache.
/// This allow the renderer to read the cache, or to initialize it if not set.
/// The cache must be returned by the function
pub fn with<R>(
&self,
f: impl FnOnce(Option<VBox<RenderingDataVTable>>) -> (Option<VBox<RenderingDataVTable>>, R),
) -> R {
let (old, r) = f(self.0.take());
let old = self.0.replace(old);
assert!(old.is_none());
r
}
}
#[cfg(feature = "ffi")]
pub(crate) mod ffi {
#![allow(unsafe_code)]

View file

@ -53,6 +53,7 @@ pub mod rendering_metrics_collector;
/// an item, which is typically computed by accessing properties. The dependency_tracker is used to allow
/// for a lazy computation. Typically back ends store either compute intensive data or handles that refer to
/// data that's stored in GPU memory.
#[derive(Default)]
pub struct CachedGraphicsData<T> {
/// The backend specific data.
pub data: T,

View file

@ -6,16 +6,17 @@
use super::graphics::RenderingCache;
use super::items::*;
use crate::component::ComponentRc;
use crate::component::{ComponentRc, RenderingDataVTable};
use crate::graphics::{CachedGraphicsData, Point, Rect};
use crate::item_tree::{
ItemRc, ItemVisitor, ItemVisitorResult, ItemVisitorVTable, VisitChildrenResult,
};
use crate::properties::PropertyTracker;
use crate::Coord;
use alloc::boxed::Box;
use core::cell::{Cell, RefCell};
use core::pin::Pin;
use vtable::VRc;
use vtable::{VBox, VRc};
/// This structure must be present in items that are Rendered and contains information.
/// Used by the backend.
@ -116,7 +117,9 @@ pub fn render_item_children(
|component: &ComponentRc, index: usize, item: Pin<ItemRef>| -> VisitChildrenResult {
renderer.save_state();
let (do_draw, item_geometry) = renderer.filter_item(item);
let item_rc = ItemRc::new(component.clone(), index);
let (do_draw, item_geometry) = renderer.filter_item(&item_rc);
let item_origin = item_geometry.origin;
renderer.translate(item_origin.x, item_origin.y);
@ -128,10 +131,7 @@ pub fn render_item_children(
// HACK, the geometry of the box shadow does not include the shadow, because when the shadow is the root for repeated elements it would translate the children
|| ItemRef::downcast_pin::<BoxShadow>(item).is_some()
{
item.as_ref().render(
&mut (renderer as &mut dyn ItemRenderer),
&ItemRc::new(component.clone(), index),
)
item.as_ref().render(&mut (renderer as &mut dyn ItemRenderer), &item_rc)
} else {
RenderingResult::ContinueRenderingChildren
};
@ -209,15 +209,15 @@ pub fn item_children_bounding_rect(
/// draw_rectangle should draw a rectangle in `(pos.x + rect.x, pos.y + rect.y)`
#[allow(missing_docs)]
pub trait ItemRenderer {
fn draw_rectangle(&mut self, rect: Pin<&Rectangle>);
fn draw_border_rectangle(&mut self, rect: Pin<&BorderRectangle>);
fn draw_image(&mut self, image: Pin<&ImageItem>);
fn draw_clipped_image(&mut self, image: Pin<&ClippedImage>);
fn draw_text(&mut self, text: Pin<&Text>);
fn draw_text_input(&mut self, text_input: Pin<&TextInput>);
fn draw_rectangle(&mut self, rect: Pin<&Rectangle>, _self_rc: &ItemRc);
fn draw_border_rectangle(&mut self, rect: Pin<&BorderRectangle>, _self_rc: &ItemRc);
fn draw_image(&mut self, image: Pin<&ImageItem>, _self_rc: &ItemRc);
fn draw_clipped_image(&mut self, image: Pin<&ClippedImage>, _self_rc: &ItemRc);
fn draw_text(&mut self, text: Pin<&Text>, _self_rc: &ItemRc);
fn draw_text_input(&mut self, text_input: Pin<&TextInput>, _self_rc: &ItemRc);
#[cfg(feature = "std")]
fn draw_path(&mut self, path: Pin<&Path>);
fn draw_box_shadow(&mut self, box_shadow: Pin<&BoxShadow>);
fn draw_path(&mut self, path: Pin<&Path>, _self_rc: &ItemRc);
fn draw_box_shadow(&mut self, box_shadow: Pin<&BoxShadow>, _self_rc: &ItemRc);
fn visit_opacity(&mut self, opacity_item: Pin<&Opacity>, _self_rc: &ItemRc) -> RenderingResult {
self.apply_opacity(opacity_item.opacity());
RenderingResult::ContinueRenderingChildren
@ -278,11 +278,15 @@ pub trait ItemRenderer {
/// Returns
/// - if the item needs to be drawn (false means it is clipped or doesn't need to be drawn)
/// - the geometry of the item
fn filter_item(&mut self, item: Pin<ItemRef>) -> (bool, Rect) {
let item_geometry = item.as_ref().geometry();
fn filter_item(&mut self, item: &ItemRc) -> (bool, Rect) {
let item_geometry = item.borrow().as_ref().geometry();
(self.get_current_clip().intersects(&item_geometry), item_geometry)
}
fn create_rendering_data(&self, _component: &ComponentRc) -> VBox<RenderingDataVTable> {
panic!("This renderer don't have rendering data")
}
fn window(&self) -> crate::window::WindowRc;
/// Return the internal renderer
@ -297,27 +301,34 @@ pub trait ItemRenderer {
}
/// The cache that needs to be held by the Window for the partial rendering
pub type PartialRenderingCache = RenderingCache<Rect>;
pub struct PartialRenderingCache {
item_geometry_tracker: Vec<CachedGraphicsData<Rect>>,
}
impl PartialRenderingCache {
/// Initialize a cache for a given component
pub fn new(component: &ComponentRc) -> Self {
let len = ComponentRc::borrow_pin(component).as_ref().get_item_tree().len();
let item_geometry_tracker =
core::iter::repeat_with(|| CachedGraphicsData::default()).take(len).collect();
Self { item_geometry_tracker }
}
}
/// FIXME: Should actually be a region and not just a rectangle
pub type DirtyRegion = euclid::default::Box2D<Coord>;
/// Put this structure in the renderer to help with partial rendering
pub struct PartialRenderer<'a, T> {
cache: &'a mut PartialRenderingCache,
pub struct PartialRenderer<T> {
/// The region of the screen which is considered dirty and that should be repainted
pub dirty_region: DirtyRegion,
actual_renderer: T,
}
impl<'a, T> PartialRenderer<'a, T> {
impl<T: ItemRenderer> PartialRenderer<T> {
/// Create a new PartialRenderer
pub fn new(
cache: &'a mut PartialRenderingCache,
initial_dirty_region: DirtyRegion,
actual_renderer: T,
) -> Self {
Self { cache, dirty_region: initial_dirty_region, actual_renderer }
pub fn new(initial_dirty_region: DirtyRegion, actual_renderer: T) -> Self {
Self { dirty_region: initial_dirty_region, actual_renderer }
}
/// Visit the tree of item and compute what are the dirty regions
@ -326,23 +337,19 @@ impl<'a, T> PartialRenderer<'a, T> {
crate::item_tree::visit_items(
component,
crate::item_tree::TraversalOrder::BackToFront,
|_, item, _, offset| match item.cached_rendering_data_offset().get_entry(self.cache)
{
Some(CachedGraphicsData { data, dependency_tracker: Some(tr) }) => {
if tr.is_dirty() {
let geom = item.as_ref().geometry();
let old_geom = *data;
self.mark_dirty_rect(old_geom, *offset);
self.mark_dirty_rect(geom, *offset);
ItemVisitorResult::Continue(*offset + geom.origin.to_vector())
} else {
ItemVisitorResult::Continue(*offset + data.origin.to_vector())
}
}
_ => {
|component, item, index, offset| {
let item_rc = ItemRc::new(component.clone(), index);
let (old_geom, is_dirty) = self
.with_cached_entry(&item_rc, |rect, tracker, _| {
(*rect, tracker.is_dirty())
});
if is_dirty {
let geom = item.as_ref().geometry();
self.mark_dirty_rect(old_geom, *offset);
self.mark_dirty_rect(geom, *offset);
ItemVisitorResult::Continue(*offset + geom.origin.to_vector())
} else {
ItemVisitorResult::Continue(*offset + old_geom.origin.to_vector())
}
},
origin.to_vector(),
@ -356,35 +363,42 @@ impl<'a, T> PartialRenderer<'a, T> {
}
}
fn do_rendering(
cache: &mut PartialRenderingCache,
rendering_data: &CachedRenderingData,
render_fn: impl FnOnce() -> Rect,
) {
if let Some(entry) = rendering_data.get_entry(cache) {
entry
.dependency_tracker
.get_or_insert_with(|| Box::pin(crate::properties::PropertyTracker::default()))
.as_ref()
.evaluate(render_fn);
} else {
let cache_entry = crate::graphics::CachedGraphicsData::new(render_fn);
rendering_data.cache_index.set(cache.insert(cache_entry));
rendering_data.cache_generation.set(cache.generation());
}
fn do_rendering(&mut self, item_rc: &ItemRc, render_fn: impl FnOnce(&mut T) -> Rect) {
self.with_cached_entry(item_rc, |rect, tracker, actual_renderer| {
*rect = tracker.as_ref().evaluate(|| render_fn(actual_renderer))
});
}
/// Move the actual renderer
pub fn into_inner(self) -> T {
self.actual_renderer
}
fn with_cached_entry<R>(
&mut self,
item_rc: &ItemRc,
f: impl FnOnce(&mut Rect, Pin<&PropertyTracker>, &mut T) -> R,
) -> R {
let component = item_rc.component();
VRc::borrow(&component).rendering_data().with(|data| {
let mut data =
data.unwrap_or_else(|| self.actual_renderer.create_rendering_data(&component));
let c: &mut PartialRenderingCache = data.partial_rendering_cache();
let entry = &mut c.item_geometry_tracker[item_rc.index()];
let tracker =
entry.dependency_tracker.get_or_insert_with(|| Box::pin(Default::default()));
let r = f(&mut entry.data, tracker.as_ref(), &mut self.actual_renderer);
(Some(data), r)
})
}
}
macro_rules! forward_rendering_call {
(fn $fn:ident($Ty:ty)) => {
fn $fn(&mut self, obj: Pin<&$Ty>) {
Self::do_rendering(&mut self.cache, &obj.cached_rendering_data, || {
self.actual_renderer.$fn(obj);
fn $fn(&mut self, obj: Pin<&$Ty>, item_rc: &ItemRc) {
self.do_rendering(item_rc, |actual_renderer| {
actual_renderer.$fn(obj, item_rc);
type Ty = $Ty;
let width = Ty::FIELD_OFFSETS.width.apply_pin(obj).get_untracked();
let height = Ty::FIELD_OFFSETS.height.apply_pin(obj).get_untracked();
@ -396,26 +410,12 @@ macro_rules! forward_rendering_call {
};
}
impl<'a, T: ItemRenderer> ItemRenderer for PartialRenderer<'a, T> {
fn filter_item(&mut self, item: Pin<ItemRef>) -> (bool, Rect) {
let rendering_data = item.cached_rendering_data_offset();
let item_geometry = match rendering_data.get_entry(self.cache) {
Some(CachedGraphicsData { data, dependency_tracker }) => {
dependency_tracker
.get_or_insert_with(|| Box::pin(crate::properties::PropertyTracker::default()))
.as_ref()
.evaluate_if_dirty(|| *data = item.as_ref().geometry());
*data
}
None => {
let cache_entry =
crate::graphics::CachedGraphicsData::new(|| item.as_ref().geometry());
let geom = cache_entry.data;
rendering_data.cache_index.set(self.cache.insert(cache_entry));
rendering_data.cache_generation.set(self.cache.generation());
geom
}
};
impl<T: ItemRenderer> ItemRenderer for PartialRenderer<T> {
fn filter_item(&mut self, item: &ItemRc) -> (bool, Rect) {
let item_geometry = self.with_cached_entry(item, |rect, tracker, _| {
tracker.evaluate_if_dirty(|| *rect = item.borrow().as_ref().geometry());
*rect
});
//let clip = self.get_current_clip().intersection(&self.dirty_region.to_rect());
//let draw = clip.map_or(false, |r| r.intersects(&item_geometry));

View file

@ -822,7 +822,9 @@ mod tests {
use super::*;
use crate::component::{Component, ComponentRc, ComponentVTable, ComponentWeak, IndexRange};
use crate::component::{
Component, ComponentRc, ComponentRenderingData, ComponentVTable, ComponentWeak, IndexRange,
};
use crate::layout::{LayoutInfo, Orientation};
use crate::slice::Slice;
@ -885,6 +887,10 @@ mod tests {
self.subtrees.borrow()[subtree_index][component_index].clone(),
))
}
fn rendering_data(&self) -> &ComponentRenderingData {
unimplemented!("Not needed for this test")
}
}
crate::component::ComponentVTable_static!(static TEST_COMPONENT_VT for TestComponent);

View file

@ -220,9 +220,9 @@ impl Item for Rectangle {
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_rectangle(self);
(*backend).draw_rectangle(self, self_rc);
RenderingResult::ContinueRenderingChildren
}
}
@ -294,9 +294,9 @@ impl Item for BorderRectangle {
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_border_rectangle(self);
(*backend).draw_border_rectangle(self, self_rc);
RenderingResult::ContinueRenderingChildren
}
}
@ -1091,9 +1091,9 @@ impl Item for BoxShadow {
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_box_shadow(self);
(*backend).draw_box_shadow(self, self_rc);
RenderingResult::ContinueRenderingChildren
}
}

View file

@ -89,9 +89,9 @@ impl Item for ImageItem {
fn render(
self: Pin<&Self>,
backend: &mut &mut dyn ItemRenderer,
_self_rc: &ItemRc,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_image(self);
(*backend).draw_image(self, self_rc);
RenderingResult::ContinueRenderingChildren
}
}
@ -173,9 +173,9 @@ impl Item for ClippedImage {
fn render(
self: Pin<&Self>,
backend: &mut &mut dyn ItemRenderer,
_self_rc: &ItemRc,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_clipped_image(self);
(*backend).draw_clipped_image(self, self_rc);
RenderingResult::ContinueRenderingChildren
}
}

View file

@ -97,14 +97,14 @@ impl Item for Path {
fn render(
self: Pin<&Self>,
backend: &mut ItemRendererRef,
_self_rc: &ItemRc,
self_rc: &ItemRc,
) -> RenderingResult {
let clip = self.clip();
if clip {
(*backend).save_state();
(*backend).combine_clip(self.geometry(), 0 as _, 0 as _)
}
(*backend).draw_path(self);
(*backend).draw_path(self, self_rc);
if clip {
(*backend).restore_state();
}

View file

@ -127,9 +127,9 @@ impl Item for Text {
fn render(
self: Pin<&Self>,
backend: &mut &mut dyn ItemRenderer,
_self_rc: &ItemRc,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_text(self);
(*backend).draw_text(self, self_rc);
RenderingResult::ContinueRenderingChildren
}
}
@ -413,9 +413,9 @@ impl Item for TextInput {
fn render(
self: Pin<&Self>,
backend: &mut &mut dyn ItemRenderer,
_self_rc: &ItemRc,
self_rc: &ItemRc,
) -> RenderingResult {
(*backend).draw_text_input(self);
(*backend).draw_text_input(self, self_rc);
RenderingResult::ContinueRenderingChildren
}
}

View file

@ -15,7 +15,8 @@ use i_slint_compiler::*;
use i_slint_compiler::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration};
use i_slint_core::api::Window;
use i_slint_core::component::{
Component, ComponentRef, ComponentRefPin, ComponentVTable, ComponentWeak, IndexRange,
Component, ComponentRef, ComponentRefPin, ComponentRenderingData, ComponentVTable,
ComponentWeak, IndexRange,
};
use i_slint_core::item_tree::{
ItemRc, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, ItemWeak, TraversalOrder,
@ -199,6 +200,10 @@ impl Component for ErasedComponentBox {
fn subtree_index(self: Pin<&Self>) -> usize {
self.borrow().as_ref().subtree_index()
}
fn rendering_data(&self) -> &ComponentRenderingData {
unsafe { rendering_data(Pin::into_inner(self.borrow())) }
}
}
i_slint_core::ComponentVTable_static!(static COMPONENT_BOX_VT for ErasedComponentBox);
@ -210,6 +215,7 @@ pub(crate) struct ComponentExtraData {
once_cell::unsync::OnceCell<vtable::VWeak<ComponentVTable, ErasedComponentBox>>,
// resource id -> file path
pub(crate) embedded_file_resources: HashMap<usize, String>,
rendering_data: ComponentRenderingData,
}
struct ErasedRepeaterWithinComponent<'id>(RepeaterWithinComponent<'id, 'static>);
@ -1049,6 +1055,7 @@ pub(crate) fn generate_component<'id>(
get_subtree_component,
parent_node,
subtree_index,
rendering_data,
drop_in_place,
dealloc,
};
@ -1521,6 +1528,16 @@ extern "C" fn layout_info(component: ComponentRefPin, orientation: Orientation)
result
}
unsafe extern "C" fn rendering_data<'a>(component: ComponentRef<'a>) -> &'a ComponentRenderingData {
let instance = component.as_ptr() as *const Instance<'a>;
let component_type =
component.get_vtable() as *const ComponentVTable as *const ComponentDescription<'a>;
// Safety: we now that this function can only be called with a component created as a dynamic component
// and that therefore the component points to an instance and its vtable to a ComponentDescription
let extra_data = (*component_type).extra_data_offset.apply(&*instance);
&extra_data.rendering_data
}
unsafe extern "C" fn get_item_ref(component: ComponentRefPin, index: usize) -> Pin<ItemRef> {
let tree = get_item_tree(component);
match &tree[index] {