iris: eliminate retained-frame heap churn
This commit is contained in:
1 parent
acf206ef19
commit
32f6ad8c79
20 files changed
+518
-195
No files matched your search
@@ -617,6 +617,15 @@ impl From<&TextAttrs> for TextShapeAttrs {
|
||||
}
|
||||
}
|
||||
|
||||
impl TextShapeAttrs {
|
||||
fn matches(&self, attrs: &TextAttrs) -> bool {
|
||||
self.color == attrs.color
|
||||
&& self.font_size == attrs.font_size
|
||||
&& self.line_height == attrs.line_height
|
||||
&& self.family == attrs.family
|
||||
}
|
||||
}
|
||||
|
||||
pub const LINE_HEIGHT_MULT: f32 = 1.1;
|
||||
|
||||
impl Default for TextAttrs {
|
||||
@@ -701,10 +710,16 @@ impl TextBuffer {
|
||||
width: Option<f32>,
|
||||
density: f32,
|
||||
) {
|
||||
let shape_attrs = TextShapeAttrs::from(attrs);
|
||||
if self.shaped.as_ref() == Some(&(shape_attrs.clone(), width, density)) {
|
||||
if self
|
||||
.shaped
|
||||
.as_ref()
|
||||
.is_some_and(|(old, old_width, old_density)| {
|
||||
old.matches(attrs) && *old_width == width && *old_density == density
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
let shape_attrs = TextShapeAttrs::from(attrs);
|
||||
let base_family = data.resolve_family(&attrs.family);
|
||||
let span_families: Vec<Option<String>> = self
|
||||
.spans
|
||||
|
||||
@@ -34,6 +34,14 @@ pub struct TextureHandle {
|
||||
rsc: RscHandle<TextureRsc>,
|
||||
}
|
||||
|
||||
impl PartialEq for TextureHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.rsc.id() == other.rsc.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for TextureHandle {}
|
||||
|
||||
/// a texture manager for a ui
|
||||
/// note that this is heavily oriented towards wgpu's renderer so the primitives don't need mapped
|
||||
pub struct Textures {
|
||||
|
||||
+15
-5
@@ -112,6 +112,14 @@ impl WgpuErrorLog {
|
||||
pub fn snapshot(&self) -> Vec<String> {
|
||||
self.errors.lock().unwrap().iter().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.errors.lock().unwrap().len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.errors.lock().unwrap().is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UiRenderNode {
|
||||
@@ -206,11 +214,13 @@ impl UiRenderNode {
|
||||
rlayer.order.update(device, queue, entries, dirty);
|
||||
let (entries, dirty) = order.images_for_upload();
|
||||
rlayer.images.update(device, queue, entries, dirty);
|
||||
rlayer.image_tex_indices = order
|
||||
.images()
|
||||
.iter()
|
||||
.map(|&slot| ui_render.primitives.instance(slot).idx)
|
||||
.collect();
|
||||
rlayer.image_tex_indices.clear();
|
||||
rlayer.image_tex_indices.extend(
|
||||
order
|
||||
.images()
|
||||
.iter()
|
||||
.map(|&slot| ui_render.primitives.instance(slot).idx),
|
||||
);
|
||||
order.updated = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,19 +450,26 @@ impl LayerOrder {
|
||||
}
|
||||
|
||||
pub fn apply_free(&mut self) -> Vec<OrderChange> {
|
||||
let mut changes = Self::apply_free_list(
|
||||
let mut changes = Vec::new();
|
||||
self.apply_free_into(&mut changes);
|
||||
changes
|
||||
}
|
||||
|
||||
pub(crate) fn apply_free_into(&mut self, changes: &mut Vec<OrderChange>) {
|
||||
Self::apply_free_list(
|
||||
&mut self.free,
|
||||
&mut self.order,
|
||||
&mut self.order_dirty,
|
||||
false,
|
||||
changes,
|
||||
);
|
||||
changes.extend(Self::apply_free_list(
|
||||
Self::apply_free_list(
|
||||
&mut self.image_free,
|
||||
&mut self.images,
|
||||
&mut self.images_dirty,
|
||||
true,
|
||||
));
|
||||
changes
|
||||
changes,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn order_for_upload(&mut self) -> (&[u32], &mut Dirty) {
|
||||
@@ -478,22 +485,20 @@ impl LayerOrder {
|
||||
list: &mut Vec<u32>,
|
||||
dirty: &mut Dirty,
|
||||
is_image: bool,
|
||||
) -> Vec<OrderChange> {
|
||||
changes: &mut Vec<OrderChange>,
|
||||
) {
|
||||
free.sort_by(|a, b| b.cmp(a));
|
||||
free.drain(..)
|
||||
.filter_map(|pos| {
|
||||
list.swap_remove(pos);
|
||||
if pos == list.len() {
|
||||
return None;
|
||||
}
|
||||
for pos in free.drain(..) {
|
||||
list.swap_remove(pos);
|
||||
if pos != list.len() {
|
||||
dirty.mark(pos);
|
||||
Some(OrderChange {
|
||||
changes.push(OrderChange {
|
||||
slot: list[pos],
|
||||
is_image,
|
||||
pos,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn order(&self) -> &Vec<u32> {
|
||||
@@ -523,7 +528,7 @@ pub enum Drawn {
|
||||
|
||||
pub const NOT_DRAWN: usize = usize::MAX;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PrimitiveHandle {
|
||||
pub layer: usize,
|
||||
pub pos: usize,
|
||||
|
||||
@@ -90,13 +90,13 @@ impl<T: Pod> ArrBuf<T> {
|
||||
}
|
||||
self.len = data.len();
|
||||
let stride = std::mem::size_of::<T>() as BufferAddress;
|
||||
for range in dirty.ranges(data.len(), Self::MERGE_GAP) {
|
||||
dirty.for_each_range(data.len(), Self::MERGE_GAP, |range| {
|
||||
queue.write_buffer(
|
||||
&self.buffer,
|
||||
range.start as BufferAddress * stride,
|
||||
bytemuck::cast_slice(&data[range]),
|
||||
);
|
||||
}
|
||||
});
|
||||
dirty.clear();
|
||||
reallocated
|
||||
}
|
||||
|
||||
+57
-22
@@ -10,11 +10,11 @@ fn node_id(id: WidgetId) -> NodeId {
|
||||
NodeId(id.as_u64())
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct Entry {
|
||||
name: String,
|
||||
role: Role,
|
||||
bounds: PixelRegion,
|
||||
seen: u64,
|
||||
}
|
||||
|
||||
fn entry_node(entry: &Entry) -> Node {
|
||||
@@ -37,6 +37,7 @@ fn entry_node(entry: &Entry) -> Node {
|
||||
#[derive(Default)]
|
||||
pub struct AccessTree {
|
||||
known: HashMap<WidgetId, Entry>,
|
||||
generation: u64,
|
||||
rebuilds: u64,
|
||||
}
|
||||
|
||||
@@ -45,12 +46,17 @@ impl AccessTree {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn collect(
|
||||
widgets: &Widgets,
|
||||
render: &UiRenderState,
|
||||
rsc: &dyn UiRsc,
|
||||
) -> HashMap<WidgetId, Entry> {
|
||||
let mut current = HashMap::default();
|
||||
/// Refresh the retained accessibility state and report whether it changed.
|
||||
/// Existing entries are updated in place so an ordinary frame allocates
|
||||
/// nothing, including one where only bounds changed.
|
||||
pub fn refresh(&mut self, widgets: &Widgets, render: &UiRenderState, rsc: &dyn UiRsc) -> bool {
|
||||
self.generation = self.generation.wrapping_add(1);
|
||||
if self.generation == 0 {
|
||||
self.known.clear();
|
||||
self.generation = 1;
|
||||
}
|
||||
let generation = self.generation;
|
||||
let mut changed = false;
|
||||
for id in widgets.named() {
|
||||
let Some(bounds) = render.window_region(&id, rsc) else {
|
||||
continue;
|
||||
@@ -58,16 +64,42 @@ impl AccessTree {
|
||||
let Some(widget) = widgets.get_dyn(id) else {
|
||||
continue;
|
||||
};
|
||||
current.insert(
|
||||
id,
|
||||
Entry {
|
||||
name: widgets.label(id).clone(),
|
||||
role: widget.access_role(),
|
||||
bounds,
|
||||
},
|
||||
);
|
||||
let name = widgets.label(id);
|
||||
let role = widget.access_role();
|
||||
match self.known.get_mut(&id) {
|
||||
Some(entry) => {
|
||||
if entry.name != *name {
|
||||
entry.name.clone_from(name);
|
||||
changed = true;
|
||||
}
|
||||
if entry.role != role || entry.bounds != bounds {
|
||||
entry.role = role;
|
||||
entry.bounds = bounds;
|
||||
changed = true;
|
||||
}
|
||||
entry.seen = generation;
|
||||
}
|
||||
None => {
|
||||
self.known.insert(
|
||||
id,
|
||||
Entry {
|
||||
name: name.clone(),
|
||||
role,
|
||||
bounds,
|
||||
seen: generation,
|
||||
},
|
||||
);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
current
|
||||
let old_len = self.known.len();
|
||||
self.known.retain(|_, entry| entry.seen == generation);
|
||||
changed |= self.known.len() != old_len;
|
||||
if changed {
|
||||
self.rebuilds += 1;
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
/// Walks `widgets.named()`, looks up each one's current screen bounds
|
||||
@@ -84,13 +116,14 @@ impl AccessTree {
|
||||
render: &UiRenderState,
|
||||
rsc: &dyn UiRsc,
|
||||
) -> Option<TreeUpdate> {
|
||||
let current = Self::collect(widgets, render, rsc);
|
||||
if current == self.known {
|
||||
if !self.refresh(widgets, render, rsc) {
|
||||
return None;
|
||||
}
|
||||
self.known = current.clone();
|
||||
self.rebuilds += 1;
|
||||
Some(build_update(¤t))
|
||||
Some(self.tree_update())
|
||||
}
|
||||
|
||||
pub fn tree_update(&self) -> TreeUpdate {
|
||||
build_update(&self.known)
|
||||
}
|
||||
|
||||
/// The unconditional twin of `update`, for a platform adapter's
|
||||
@@ -100,7 +133,9 @@ impl AccessTree {
|
||||
/// meant to answer (it may have already sent this same snapshot to a
|
||||
/// client that has since detached and reattached).
|
||||
pub fn build_full(widgets: &Widgets, render: &UiRenderState, rsc: &dyn UiRsc) -> TreeUpdate {
|
||||
build_update(&Self::collect(widgets, render, rsc))
|
||||
let mut tree = Self::new();
|
||||
tree.refresh(widgets, render, rsc);
|
||||
tree.tree_update()
|
||||
}
|
||||
|
||||
pub fn take_rebuilds(&mut self) -> u64 {
|
||||
|
||||
@@ -9,12 +9,16 @@ pub struct ActiveData {
|
||||
pub region: UiRegion,
|
||||
pub parent: Option<WidgetId>,
|
||||
pub textures: Vec<TextureHandle>,
|
||||
pub(crate) spare_textures: Vec<TextureHandle>,
|
||||
/// Paint slots retained by this draw. The GPU primitive stores only the
|
||||
/// slot index, so these handles are what prevent a live primitive from
|
||||
/// observing a recycled paint.
|
||||
pub paints: Vec<PaintId>,
|
||||
pub(crate) spare_paints: Vec<PaintId>,
|
||||
pub primitives: Vec<PrimitiveHandle>,
|
||||
pub(crate) spare_primitives: Vec<PrimitiveHandle>,
|
||||
pub children: Vec<WidgetId>,
|
||||
pub(crate) spare_children: Vec<WidgetId>,
|
||||
pub size_dependencies: Vec<WidgetId>,
|
||||
pub mask: MaskIdx,
|
||||
/// The widget's retained mask slot, or `MaskIdx::NONE`.
|
||||
|
||||
+4
-6
@@ -122,13 +122,11 @@ impl Ui {
|
||||
update(&mut text)?;
|
||||
text.invalidate_all()
|
||||
};
|
||||
let active: Vec<WidgetId> = {
|
||||
let mut active = owners;
|
||||
{
|
||||
let render = self.render_state.get();
|
||||
owners
|
||||
.into_iter()
|
||||
.filter(|owner| render.active.contains_key(owner))
|
||||
.collect()
|
||||
};
|
||||
active.retain(|owner| render.active.contains_key(owner));
|
||||
}
|
||||
self.data.widgets.needs_redraw.extend(active);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+28
-9
@@ -21,9 +21,12 @@ pub struct Painter<'a> {
|
||||
pub(super) child_move_slot: Option<MoveIdx>,
|
||||
pub(super) own_mask: MaskIdx,
|
||||
pub(super) textures: Vec<TextureHandle>,
|
||||
pub(super) recycle_textures: Vec<TextureHandle>,
|
||||
pub(super) paints: Vec<PaintId>,
|
||||
pub(super) recycle_paints: Vec<PaintId>,
|
||||
pub(super) primitives: Vec<PrimitiveHandle>,
|
||||
pub(super) recycle: std::iter::Peekable<std::vec::IntoIter<PrimitiveHandle>>,
|
||||
pub(super) recycle: Vec<PrimitiveHandle>,
|
||||
pub(super) recycle_at: usize,
|
||||
pub(super) children: Vec<WidgetId>,
|
||||
pub(super) size_dependencies: Vec<WidgetId>,
|
||||
pub(super) size: Option<Size>,
|
||||
@@ -87,12 +90,13 @@ impl<'a> Painter<'a> {
|
||||
/// anyway. Stopping is also what keeps the invariant simple: every
|
||||
/// handle from `recycled` on is untouched and gets freed together.
|
||||
fn take_recycled(&mut self, binding: u32, drawn: Drawn) -> Option<PrimitiveHandle> {
|
||||
let h = self.recycle.peek()?;
|
||||
let h = *self.recycle.get(self.recycle_at)?;
|
||||
let drawn_matches = (h.pos == NOT_DRAWN) == (drawn == Drawn::No);
|
||||
if h.binding != binding || h.layer != self.layer || !drawn_matches {
|
||||
return None;
|
||||
}
|
||||
self.recycle.next()
|
||||
self.recycle_at += 1;
|
||||
Some(h)
|
||||
}
|
||||
|
||||
fn write_primitive<P: Primitive>(
|
||||
@@ -139,14 +143,18 @@ impl<'a> Painter<'a> {
|
||||
/// primitive and retains the handle for exactly as long as that draw.
|
||||
pub fn paint(&mut self, paint: &PaintId) -> u32 {
|
||||
if !self.paints.contains(paint) {
|
||||
self.paints.push(paint.clone());
|
||||
if let Some(i) = self.recycle_paints.iter().position(|old| old == paint) {
|
||||
self.paints.push(self.recycle_paints.swap_remove(i));
|
||||
} else {
|
||||
self.paints.push(paint.clone());
|
||||
}
|
||||
}
|
||||
paint.slot()
|
||||
}
|
||||
|
||||
pub fn paint_value(&mut self, paint: &mut crate::PaintValue) -> u32 {
|
||||
let paint = paint.resolve(&mut self.rsc.ui_mut().paints).clone();
|
||||
self.paint(&paint)
|
||||
let paint = paint.resolve(&mut self.rsc.ui_mut().paints);
|
||||
self.paint(paint)
|
||||
}
|
||||
|
||||
pub fn primitive_within<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
||||
@@ -387,20 +395,31 @@ impl<'a> Painter<'a> {
|
||||
}
|
||||
|
||||
pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) {
|
||||
self.textures.push(handle.clone());
|
||||
self.retain_texture(handle);
|
||||
self.write_image(handle.image_index(), region.within(&self.region));
|
||||
}
|
||||
|
||||
pub fn texture(&mut self, handle: &TextureHandle) {
|
||||
self.textures.push(handle.clone());
|
||||
self.retain_texture(handle);
|
||||
self.write_image(handle.image_index(), self.region);
|
||||
}
|
||||
|
||||
pub fn texture_at(&mut self, handle: &TextureHandle, region: UiRegion) {
|
||||
self.textures.push(handle.clone());
|
||||
self.retain_texture(handle);
|
||||
self.write_image(handle.image_index(), region);
|
||||
}
|
||||
|
||||
fn retain_texture(&mut self, handle: &TextureHandle) {
|
||||
if self.textures.contains(handle) {
|
||||
return;
|
||||
}
|
||||
if let Some(i) = self.recycle_textures.iter().position(|old| old == handle) {
|
||||
self.textures.push(self.recycle_textures.swap_remove(i));
|
||||
} else {
|
||||
self.textures.push(handle.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn write_image(&mut self, texture_idx: u32, region: UiRegion) {
|
||||
let h = match self.take_recycled(IMAGE_BINDING, Drawn::Yes) {
|
||||
Some(h) => {
|
||||
|
||||
+82
-21
@@ -5,8 +5,8 @@ use crate::{
|
||||
ActiveData, Axis, ChildOrder, IdLike, MaskIdx, MoveIdx, Painter, PixelRegion, PrimitiveLayers,
|
||||
RegionAlign, Size, StrongWidget, UiRegion, UiRsc, UiVec2, Widget, WidgetId, Widgets,
|
||||
render::{
|
||||
Drawn, MoveOffset, NOT_DRAWN, Primitive, PrimitiveHandle, PrimitiveInst, Primitives,
|
||||
RectPrimitive, rounded_rect_coverage,
|
||||
Drawn, MoveOffset, NOT_DRAWN, OrderChange, Primitive, PrimitiveHandle, PrimitiveInst,
|
||||
Primitives, RectPrimitive, rounded_rect_coverage,
|
||||
},
|
||||
util::{HashMap, HashSet, Id, Vec2},
|
||||
};
|
||||
@@ -30,6 +30,7 @@ pub struct UiRenderState {
|
||||
pub active: HashMap<WidgetId, ActiveData>,
|
||||
pub primitives: Primitives,
|
||||
pub layers: PrimitiveLayers,
|
||||
order_changes: Vec<OrderChange>,
|
||||
pub(super) output_size: Vec2,
|
||||
/// Physical pixels per `dp` -- see `LayoutLen::dp`'s field doc. `1.0` (an
|
||||
/// unscaled display) until a backend that knows its own density calls
|
||||
@@ -80,11 +81,17 @@ pub struct UiRenderState {
|
||||
pub(crate) struct Retained {
|
||||
pub region: Option<UiRegion>,
|
||||
pub children: Vec<WidgetId>,
|
||||
pub spare_children: Vec<WidgetId>,
|
||||
pub move_slot: Option<MoveIdx>,
|
||||
pub child_move_slot: Option<MoveIdx>,
|
||||
pub own_mask: MaskIdx,
|
||||
pub textures: Vec<crate::TextureHandle>,
|
||||
pub spare_textures: Vec<crate::TextureHandle>,
|
||||
pub primitives: Vec<PrimitiveHandle>,
|
||||
pub spare_primitives: Vec<PrimitiveHandle>,
|
||||
pub paints: Vec<crate::PaintId>,
|
||||
pub spare_paints: Vec<crate::PaintId>,
|
||||
pub size_dependencies: Vec<WidgetId>,
|
||||
}
|
||||
|
||||
impl Default for Retained {
|
||||
@@ -92,11 +99,17 @@ impl Default for Retained {
|
||||
Self {
|
||||
region: None,
|
||||
children: Vec::new(),
|
||||
spare_children: Vec::new(),
|
||||
move_slot: None,
|
||||
child_move_slot: None,
|
||||
own_mask: MaskIdx::NONE,
|
||||
textures: Vec::new(),
|
||||
spare_textures: Vec::new(),
|
||||
primitives: Vec::new(),
|
||||
spare_primitives: Vec::new(),
|
||||
paints: Vec::new(),
|
||||
spare_paints: Vec::new(),
|
||||
size_dependencies: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,6 +122,7 @@ impl UiRenderState {
|
||||
active: Default::default(),
|
||||
primitives: Default::default(),
|
||||
layers: Default::default(),
|
||||
order_changes: Vec::new(),
|
||||
output_size: Vec2::ZERO,
|
||||
density: 1.0,
|
||||
old_root: None,
|
||||
@@ -184,8 +198,11 @@ impl UiRenderState {
|
||||
}
|
||||
|
||||
fn apply_free(&mut self) {
|
||||
let mut changes = std::mem::take(&mut self.order_changes);
|
||||
for (layer, order) in self.layers.iter_mut() {
|
||||
for change in order.apply_free() {
|
||||
changes.clear();
|
||||
order.apply_free_into(&mut changes);
|
||||
for change in changes.drain(..) {
|
||||
let owner = self.primitives.owner(change.slot);
|
||||
let Some(idx) = self.primitives.handle_index(change.slot) else {
|
||||
continue;
|
||||
@@ -204,6 +221,7 @@ impl UiRenderState {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.order_changes = changes;
|
||||
self.primitives.release_freed();
|
||||
}
|
||||
|
||||
@@ -380,11 +398,17 @@ impl UiRenderState {
|
||||
let Retained {
|
||||
region: mut old_region,
|
||||
children: mut old_children,
|
||||
spare_children: mut children,
|
||||
move_slot: mut old_move_slot,
|
||||
mut child_move_slot,
|
||||
mut own_mask,
|
||||
textures: mut recycle_textures,
|
||||
spare_textures: mut textures,
|
||||
primitives: mut recycle,
|
||||
paints: _old_paints,
|
||||
spare_primitives: mut primitives,
|
||||
paints: mut recycle_paints,
|
||||
spare_paints: mut paints,
|
||||
mut size_dependencies,
|
||||
} = retained;
|
||||
let dirty = rsc.widgets_mut().needs_redraw.remove(&id);
|
||||
let requires_exact_region = rsc
|
||||
@@ -434,10 +458,17 @@ impl UiRenderState {
|
||||
let active = self.remove(id, false, true, rsc).unwrap();
|
||||
old_region = Some(active.region);
|
||||
old_children = active.children;
|
||||
children = active.spare_children;
|
||||
old_move_slot = Some(active.move_slot);
|
||||
child_move_slot = active.child_move_slot;
|
||||
own_mask = active.own_mask;
|
||||
recycle_textures = active.textures;
|
||||
textures = active.spare_textures;
|
||||
recycle = active.primitives;
|
||||
primitives = active.spare_primitives;
|
||||
recycle_paints = active.paints;
|
||||
paints = active.spare_paints;
|
||||
size_dependencies = active.size_dependencies;
|
||||
} else if self.active.contains_key(&id) {
|
||||
let layer_changed = self
|
||||
.active
|
||||
@@ -449,12 +480,25 @@ impl UiRenderState {
|
||||
}
|
||||
old_region = Some(active.region);
|
||||
old_children = active.children;
|
||||
children = active.spare_children;
|
||||
old_move_slot = Some(active.move_slot);
|
||||
child_move_slot = active.child_move_slot;
|
||||
own_mask = active.own_mask;
|
||||
recycle_textures = active.textures;
|
||||
textures = active.spare_textures;
|
||||
recycle = active.primitives;
|
||||
primitives = active.spare_primitives;
|
||||
recycle_paints = active.paints;
|
||||
paints = active.spare_paints;
|
||||
size_dependencies = active.size_dependencies;
|
||||
}
|
||||
|
||||
textures.clear();
|
||||
paints.clear();
|
||||
primitives.clear();
|
||||
children.clear();
|
||||
size_dependencies.clear();
|
||||
|
||||
let reentrant = !self.draw_started.insert(id);
|
||||
debug_assert!(
|
||||
!reentrant,
|
||||
@@ -481,12 +525,15 @@ impl UiRenderState {
|
||||
own_mask,
|
||||
layer,
|
||||
id,
|
||||
textures: Vec::new(),
|
||||
paints: Vec::new(),
|
||||
primitives: Vec::new(),
|
||||
recycle: recycle.into_iter().peekable(),
|
||||
children: Vec::new(),
|
||||
size_dependencies: Vec::new(),
|
||||
textures,
|
||||
recycle_textures,
|
||||
paints,
|
||||
recycle_paints,
|
||||
primitives,
|
||||
recycle,
|
||||
recycle_at: 0,
|
||||
children,
|
||||
size_dependencies,
|
||||
size: None,
|
||||
reuse_child_sizes,
|
||||
rsc,
|
||||
@@ -534,9 +581,12 @@ impl UiRenderState {
|
||||
child_move_slot,
|
||||
own_mask,
|
||||
textures,
|
||||
mut recycle_textures,
|
||||
paints,
|
||||
mut recycle_paints,
|
||||
primitives,
|
||||
recycle,
|
||||
mut recycle,
|
||||
recycle_at,
|
||||
children,
|
||||
size_dependencies,
|
||||
size: _,
|
||||
@@ -545,18 +595,31 @@ impl UiRenderState {
|
||||
id,
|
||||
} = painter;
|
||||
|
||||
for h in recycle {
|
||||
self.free_primitive(&h);
|
||||
for h in &recycle[recycle_at..] {
|
||||
self.free_primitive(h);
|
||||
}
|
||||
for c in &old_children {
|
||||
if !children.contains(c) {
|
||||
self.remove_rec(*c, rsc);
|
||||
}
|
||||
}
|
||||
recycle.clear();
|
||||
recycle_textures.clear();
|
||||
recycle_paints.clear();
|
||||
old_children.clear();
|
||||
|
||||
let active = ActiveData {
|
||||
id,
|
||||
region,
|
||||
parent,
|
||||
textures,
|
||||
spare_textures: recycle_textures,
|
||||
paints,
|
||||
spare_paints: recycle_paints,
|
||||
primitives,
|
||||
spare_primitives: recycle,
|
||||
children,
|
||||
spare_children: old_children,
|
||||
size_dependencies,
|
||||
mask: inherited_mask,
|
||||
layer: inherited_layer,
|
||||
@@ -567,12 +630,6 @@ impl UiRenderState {
|
||||
move_applied: Vec2::ZERO,
|
||||
};
|
||||
|
||||
for c in &old_children {
|
||||
if !active.children.contains(c) {
|
||||
self.remove_rec(*c, rsc);
|
||||
}
|
||||
}
|
||||
|
||||
rsc.on_draw(&active);
|
||||
self.active.insert(id, active);
|
||||
size
|
||||
@@ -713,8 +770,6 @@ impl UiRenderState {
|
||||
}
|
||||
}
|
||||
Self::remask_shape_users(&self.active, id, active.own_mask, &active.primitives, rsc);
|
||||
active.textures.clear();
|
||||
rsc.ui_mut().textures.free();
|
||||
if undraw {
|
||||
// A captured widget that goes away mid-gesture (LazySpan's
|
||||
// virtualisation retiring a row, a rebuild) must not leave
|
||||
@@ -1137,11 +1192,17 @@ impl UiRenderState {
|
||||
Retained {
|
||||
region: Some(active.region),
|
||||
children: active.children,
|
||||
spare_children: active.spare_children,
|
||||
move_slot: Some(active.move_slot),
|
||||
child_move_slot: active.child_move_slot,
|
||||
own_mask: active.own_mask,
|
||||
textures: active.textures,
|
||||
spare_textures: active.spare_textures,
|
||||
primitives: active.primitives,
|
||||
spare_primitives: active.spare_primitives,
|
||||
paints: active.paints,
|
||||
spare_paints: active.spare_paints,
|
||||
size_dependencies: active.size_dependencies,
|
||||
},
|
||||
rsc,
|
||||
);
|
||||
|
||||
+25
-5
@@ -62,10 +62,24 @@ impl Dirty {
|
||||
}
|
||||
|
||||
pub fn ranges(&self, len: usize, gap: usize) -> Vec<Range<usize>> {
|
||||
let mut ranges = Vec::new();
|
||||
self.for_each_range(len, gap, |range| ranges.push(range));
|
||||
ranges
|
||||
}
|
||||
|
||||
pub(crate) fn for_each_range(
|
||||
&self,
|
||||
len: usize,
|
||||
gap: usize,
|
||||
mut visit: impl FnMut(Range<usize>),
|
||||
) {
|
||||
if self.all {
|
||||
return Vec::from_iter((len > 0).then_some(0..len));
|
||||
if len > 0 {
|
||||
visit(0..len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let mut ranges: Vec<Range<usize>> = Vec::new();
|
||||
let mut pending: Option<Range<usize>> = None;
|
||||
for (w, word) in self.words.iter().enumerate() {
|
||||
let mut bits = *word;
|
||||
while bits != 0 {
|
||||
@@ -75,14 +89,20 @@ impl Dirty {
|
||||
if start >= len {
|
||||
break;
|
||||
}
|
||||
match ranges.last_mut() {
|
||||
match pending.as_mut() {
|
||||
Some(last) if start - last.end <= gap => last.end = end,
|
||||
_ => ranges.push(start..end),
|
||||
_ => {
|
||||
if let Some(range) = pending.replace(start..end) {
|
||||
visit(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
bits &= !(((1u128 << run) - 1) as u64) << (start - w * 64);
|
||||
}
|
||||
}
|
||||
ranges
|
||||
if let Some(range) = pending {
|
||||
visit(range);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
|
||||
+12
-4
@@ -8,10 +8,18 @@ pub struct WidgetData {
|
||||
|
||||
impl WidgetData {
|
||||
pub fn new<W: Widget>(widget: W) -> Self {
|
||||
let mut label = std::any::type_name::<W>().to_string();
|
||||
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
|
||||
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
|
||||
}
|
||||
let name = std::any::type_name::<W>();
|
||||
let label = match (name.find("::"), name.rfind("::")) {
|
||||
(Some(first), Some(last)) => {
|
||||
let suffix = &name[last + 2..];
|
||||
let mut label = String::with_capacity(first + 2 + suffix.len());
|
||||
label.push_str(&name[..first]);
|
||||
label.push_str("::");
|
||||
label.push_str(suffix);
|
||||
label
|
||||
}
|
||||
_ => name.to_owned(),
|
||||
};
|
||||
Self {
|
||||
widget: Box::new(widget),
|
||||
label,
|
||||
|
||||
Reference in new issue
Block a user