mirror of
https://github.com/slint-ui/slint.git
synced 2025-12-23 09:19:32 +00:00
WIP: refactor rendering cache
This commit is contained in:
parent
9beef43a9d
commit
5ab3cdce6d
16 changed files with 226 additions and 139 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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::{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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, || {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue