use crate::graph::{
InlineNode, LayerNode, PrimitiveEntry, PrimitiveNode, RenderNode, TextPrimitiveNode,
};
#[derive(Default)]
#[expect(
clippy::vec_box,
reason = "the boxes are the allocations the pool keeps for reuse"
)]
pub(crate) struct Pool {
layers: Vec<Box<LayerNode>>,
inlines: Vec<Box<InlineNode>>,
texts: Vec<Box<TextPrimitiveNode>>,
lists: Vec<Vec<RenderNode>>,
}
impl Pool {
fn recycle_layer(&mut self, mut layer: Box<LayerNode>) {
self.recycle_children(&mut layer);
layer.hit_test = None;
self.layers.push(layer);
}
fn recycle_children(&mut self, layer: &mut LayerNode) {
self.recycle_nodes(&mut layer.children);
}
fn recycle_nodes(&mut self, nodes: &mut Vec<RenderNode>) {
while let Some(child) = nodes.pop() {
self.recycle_node(child);
}
}
fn recycle_node(&mut self, node: RenderNode) {
match node {
RenderNode::Layer(layer) => self.recycle_layer(layer),
RenderNode::Inline(inline) => self.recycle_inline(inline),
RenderNode::Primitive(PrimitiveEntry {
node: PrimitiveNode::Text(text),
..
}) => self.texts.push(text),
RenderNode::Primitive(_) | RenderNode::DrawRun(_) => {}
}
}
fn recycle_inline(&mut self, mut inline: Box<InlineNode>) {
self.recycle_nodes(&mut inline.content);
self.inlines.push(inline);
}
}
pub(crate) fn recycle_children(layer: &mut LayerNode) {
crate::thread_state::with(|state| state.layer_pool.borrow_mut().recycle_children(layer));
}
pub(crate) fn recycle_primitives(layer: &mut LayerNode) {
let children = &mut layer.children;
let is_child_layer = |child: &RenderNode| child.draw_phase().is_none();
let first_layer = children
.iter()
.position(is_child_layer)
.unwrap_or(children.len());
let end = children
.iter()
.rposition(is_child_layer)
.map_or(first_layer, |last| last + 1);
debug_assert!(
children[first_layer..end].iter().all(is_child_layer),
"a layer's own primitives never sit between its child layers"
);
if first_layer == 0 && end == children.len() {
return;
}
crate::thread_state::with(|state| {
let mut pool = state.layer_pool.borrow_mut();
children
.drain(end..)
.for_each(|child| pool.recycle_node(child));
children
.drain(..first_layer)
.for_each(|child| pool.recycle_node(child));
});
}
pub(crate) fn recycle_node(node: RenderNode) {
crate::thread_state::with(|state| state.layer_pool.borrow_mut().recycle_node(node));
}
pub(crate) fn recycle_content(inline: &mut InlineNode) {
crate::thread_state::with(|state| {
state
.layer_pool
.borrow_mut()
.recycle_nodes(&mut inline.content);
});
}
pub(crate) fn recycle_list(list: Vec<RenderNode>) {
debug_assert!(list.is_empty(), "only an emptied list is recycled");
if list.capacity() > 0 {
crate::thread_state::with(|state| state.layer_pool.borrow_mut().lists.push(list));
}
}
pub(crate) fn child_list(capacity: usize) -> Vec<RenderNode> {
let mut list = crate::thread_state::with(|state| state.layer_pool.borrow_mut().lists.pop())
.unwrap_or_default();
reserve_nodes(&mut list, capacity);
list
}
pub(crate) fn reserve_nodes(list: &mut Vec<RenderNode>, additional: usize) {
if list.capacity() == 0 {
list.reserve_exact(additional);
} else {
list.reserve(additional);
}
}
pub(crate) fn layer_box() -> Box<LayerNode> {
crate::thread_state::with(|state| state.layer_pool.borrow_mut().layers.pop())
.unwrap_or_default()
}
pub(crate) fn inline_box() -> Option<Box<InlineNode>> {
crate::thread_state::with(|state| state.layer_pool.borrow_mut().inlines.pop())
}
pub(crate) fn boxed_text(text: TextPrimitiveNode) -> Box<TextPrimitiveNode> {
match crate::thread_state::with(|state| state.layer_pool.borrow_mut().texts.pop()) {
Some(mut recycled) => {
*recycled = text;
recycled
}
None => Box::new(text),
}
}
pub(crate) fn release() {
crate::thread_state::with(|state| {
let mut pool = state.layer_pool.borrow_mut();
pool.layers.clear();
pool.inlines.clear();
pool.texts.clear();
pool.lists.clear();
});
}