mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-03 02:13:21 +00:00

This removes a lot of allocations and speeds up the compiler step a bit. Sadly, this patch is very invasive as it touches a lot of files. That said, each individual hunk is pretty trivial. For a non-trivial real-world example, the impact is significant, we get rid of ~29% of all allocations and improve the runtime by about 4.8% (measured until the viewer loop would start). Before: ``` Benchmark 1: ./target/release/slint-viewer ../slint-perf/app.slint Time (mean ± σ): 664.2 ms ± 6.7 ms [User: 589.2 ms, System: 74.0 ms] Range (min … max): 659.0 ms … 682.4 ms 10 runs allocations: 4886888 temporary allocations: 857508 ``` After: ``` Benchmark 1: ./target/release/slint-viewer ../slint-perf/app.slint Time (mean ± σ): 639.5 ms ± 17.8 ms [User: 556.9 ms, System: 76.2 ms] Range (min … max): 621.4 ms … 666.5 ms 10 runs allocations: 3544318 temporary allocations: 495685 ```
173 lines
6.8 KiB
Rust
173 lines
6.8 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
//! Pass that lowers synthetic `drop-shadow-*` properties to proper shadow elements
|
|
// At the moment only shadows on `Rectangle` elements are supported, i.e. the drop shadow
|
|
// of a rectangle is a box shadow.
|
|
|
|
use crate::diagnostics::BuildDiagnostics;
|
|
use crate::expression_tree::BindingExpression;
|
|
use crate::{expression_tree::Expression, object_tree::*};
|
|
use crate::{expression_tree::NamedReference, typeregister::TypeRegister};
|
|
use smol_str::{format_smolstr, SmolStr};
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::rc::Rc;
|
|
|
|
// Creates a new element for the drop shadow properties that'll be a sibling to the specified
|
|
// sibling element.
|
|
fn create_box_shadow_element(
|
|
shadow_property_bindings: HashMap<SmolStr, BindingExpression>,
|
|
sibling_element: &ElementRc,
|
|
type_register: &TypeRegister,
|
|
diag: &mut BuildDiagnostics,
|
|
) -> Option<Element> {
|
|
if matches!(sibling_element.borrow().builtin_type(), Some(b) if b.name != "Rectangle") {
|
|
for (shadow_prop_name, shadow_prop_binding) in shadow_property_bindings {
|
|
diag.push_error(
|
|
format!("The {shadow_prop_name} property is only supported on Rectangle elements right now"),
|
|
&shadow_prop_binding,
|
|
);
|
|
}
|
|
return None;
|
|
}
|
|
|
|
let mut element = Element {
|
|
id: format_smolstr!("{}-shadow", sibling_element.borrow().id),
|
|
base_type: type_register.lookup_builtin_element("BoxShadow").unwrap(),
|
|
enclosing_component: sibling_element.borrow().enclosing_component.clone(),
|
|
bindings: shadow_property_bindings
|
|
.into_iter()
|
|
.map(|(shadow_prop_name, expr)| {
|
|
(shadow_prop_name.strip_prefix("drop-shadow-").unwrap().into(), expr.into())
|
|
})
|
|
.collect(),
|
|
..Default::default()
|
|
};
|
|
|
|
// FIXME: remove the border-radius manual mapping.
|
|
if sibling_element.borrow().bindings.contains_key("border-radius") {
|
|
element.bindings.insert(
|
|
"border-radius".into(),
|
|
RefCell::new(
|
|
Expression::PropertyReference(NamedReference::new(
|
|
sibling_element,
|
|
"border-radius",
|
|
))
|
|
.into(),
|
|
),
|
|
);
|
|
}
|
|
|
|
Some(element)
|
|
}
|
|
|
|
// For a repeated element, this function creates a new element for the drop shadow properties that
|
|
// will act as the new root element in the repeater. The former root will become a child.
|
|
fn inject_shadow_element_in_repeated_element(
|
|
shadow_property_bindings: HashMap<SmolStr, BindingExpression>,
|
|
repeated_element: &ElementRc,
|
|
type_register: &TypeRegister,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
let element_with_shadow_property =
|
|
&repeated_element.borrow().base_type.as_component().root_element.clone();
|
|
|
|
let shadow_element = match create_box_shadow_element(
|
|
shadow_property_bindings,
|
|
element_with_shadow_property,
|
|
type_register,
|
|
diag,
|
|
) {
|
|
Some(element) => element,
|
|
None => return,
|
|
};
|
|
|
|
crate::object_tree::inject_element_as_repeated_element(
|
|
repeated_element,
|
|
Element::make_rc(shadow_element),
|
|
);
|
|
}
|
|
|
|
fn take_shadow_property_bindings(element: &ElementRc) -> HashMap<SmolStr, BindingExpression> {
|
|
crate::typeregister::RESERVED_DROP_SHADOW_PROPERTIES
|
|
.iter()
|
|
.flat_map(|(shadow_property_name, _)| {
|
|
let shadow_property_name = SmolStr::new(shadow_property_name);
|
|
let mut element = element.borrow_mut();
|
|
element.bindings.remove(&shadow_property_name).map(|binding| {
|
|
// Remove the drop-shadow property that was also materialized as a fake property by now.
|
|
element.property_declarations.remove(&shadow_property_name);
|
|
(shadow_property_name, binding.into_inner())
|
|
})
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn lower_shadow_properties(
|
|
component: &Rc<Component>,
|
|
type_register: &TypeRegister,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
for (shadow_prop_name, shadow_prop_binding) in
|
|
take_shadow_property_bindings(&component.root_element)
|
|
{
|
|
diag.push_warning(
|
|
format!("The {} property cannot be used on the root element, the shadow will not be visible", shadow_prop_name),
|
|
&shadow_prop_binding,
|
|
);
|
|
}
|
|
|
|
recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
|
|
// When encountering a repeater where the repeated element has a `drop-shadow` property, we create a new
|
|
// dedicated shadow element and make the previously repeated element a child of that. This ensures rendering
|
|
// underneath while maintaining the hierarchy for the repeater.
|
|
// The geometry properties are aliased using two-way bindings (which may be eliminated in a later pass).
|
|
|
|
if elem.borrow().repeated.is_some() {
|
|
let component = elem.borrow().base_type.as_component().clone(); // CHECK if clone can be removed if we change borrow
|
|
|
|
let drop_shadow_properties = take_shadow_property_bindings(&component.root_element);
|
|
if !drop_shadow_properties.is_empty() {
|
|
drop(component);
|
|
inject_shadow_element_in_repeated_element(
|
|
drop_shadow_properties,
|
|
elem,
|
|
type_register,
|
|
diag,
|
|
);
|
|
}
|
|
}
|
|
|
|
let old_children = {
|
|
let mut elem = elem.borrow_mut();
|
|
let new_children = Vec::with_capacity(elem.children.len());
|
|
std::mem::replace(&mut elem.children, new_children)
|
|
};
|
|
|
|
// When encountering a `drop-shadow` property in a supported element, we create a new dedicated
|
|
// shadow element and insert it *before* the element that had the `drop-shadow` property, to ensure
|
|
// that it is rendered underneath.
|
|
for child in old_children {
|
|
let drop_shadow_properties = take_shadow_property_bindings(&child);
|
|
if !drop_shadow_properties.is_empty() {
|
|
let mut shadow_elem = match create_box_shadow_element(
|
|
drop_shadow_properties,
|
|
&child,
|
|
type_register,
|
|
diag,
|
|
) {
|
|
Some(element) => element,
|
|
None => {
|
|
elem.borrow_mut().children.push(child);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
shadow_elem.geometry_props.clone_from(&child.borrow().geometry_props);
|
|
elem.borrow_mut().children.push(ElementRc::new(shadow_elem.into()));
|
|
}
|
|
elem.borrow_mut().children.push(child);
|
|
}
|
|
});
|
|
}
|