iris: share resource handle bookkeeping

This commit is contained in:
iris committed 2026-09-12 17:13:49 -04:00
1 parent 51719ed121
commit 1be6cc2248
9 files changed
+631 -217

No files matched your search

+62 -82
View File
@@ -1,6 +1,6 @@
use crate::{
Align, GlyphAtlas, GlyphKey, PaintId, PlacedGlyph, RegionAlign, Textures, WidgetId,
util::{SlotId, SlotVec, Vec2},
util::{Resources, RscHandle, StrongRscId, Vec2, WeakRscId},
};
use parley::{
Alignment, AlignmentOptions, FontContext, FontFamily, FontStyle, FontWeight, GenericFamily,
@@ -8,15 +8,12 @@ use parley::{
fontique::{Blob, FontInfoOverride},
};
use std::{
cell::{Ref, RefCell},
cell::{Ref, RefCell, RefMut},
collections::HashMap,
fmt,
ops::{Deref, DerefMut, Range},
rc::Rc,
sync::{
Arc,
mpsc::{Receiver, Sender, channel},
},
sync::Arc,
};
use swash::{
FontRef,
@@ -610,7 +607,7 @@ pub struct RenderedText {
pub generation: u64,
}
pub struct TextResource {
pub struct TextRsc {
buffer: TextBuffer,
attrs: TextAttrs,
rendered: Option<RenderedText>,
@@ -619,7 +616,7 @@ pub struct TextResource {
owner: Option<WidgetId>,
}
impl TextResource {
impl TextRsc {
fn new(buffer: TextBuffer, attrs: TextAttrs) -> Self {
Self {
buffer,
@@ -641,57 +638,54 @@ impl TextResource {
/// compact [`TextHandle`] into this arena rather than owning Parley layouts.
pub struct TextResources {
data: TextData,
entries: SlotVec<TextResource>,
send: Sender<SlotId>,
recv: Receiver<SlotId>,
entries: Rc<RefCell<Resources<TextRsc>>>,
}
impl TextResources {
pub fn new() -> Self {
let (send, recv) = channel();
Self {
data: TextData::default(),
entries: SlotVec::new(),
send,
recv,
entries: Rc::new(RefCell::new(Resources::new())),
}
}
pub fn add(resources: Rc<RefCell<Self>>, buffer: TextBuffer, attrs: TextAttrs) -> TextHandle {
let (id, send) = {
let entries = {
let mut resources = resources.borrow_mut();
resources.free_released();
let id = resources.entries.add(TextResource::new(buffer, attrs));
(id, resources.send.clone())
resources.entries.clone()
};
TextHandle {
id,
rsc: RscHandle::add(entries, TextRsc::new(buffer, attrs)),
resources,
send,
}
}
fn entry(&self, id: SlotId) -> &TextResource {
self.entries
.get(id)
.expect("text resource handle points at a released slot")
pub fn handle(resources: Rc<RefCell<Self>>, id: StrongRscId<TextRsc>) -> TextHandle {
let entries = resources.borrow().entries.clone();
TextHandle {
rsc: RscHandle::new(id, entries),
resources,
}
}
fn entry_mut(&mut self, id: SlotId) -> &mut TextResource {
self.entries
.get_mut(id)
.expect("text resource handle points at a released slot")
pub fn upgrade(resources: Rc<RefCell<Self>>, id: WeakRscId<TextRsc>) -> Option<TextHandle> {
let entries = {
let mut resources = resources.borrow_mut();
resources.free_released();
resources.entries.clone()
};
let id = entries.borrow_mut().upgrade(id)?;
Some(Self::handle(resources, id))
}
pub fn free_released(&mut self) {
for id in self.recv.try_iter() {
self.entries.free(id);
}
self.entries.borrow_mut().apply(|_, _| {});
}
pub fn invalidate_all(&mut self) -> Vec<WidgetId> {
let mut owners = Vec::new();
for resource in self.entries.values_mut() {
for resource in self.entries.borrow_mut().values_mut() {
resource.invalidate();
if let Some(owner) = resource.owner
&& !owners.contains(&owner)
@@ -702,27 +696,22 @@ impl TextResources {
owners
}
fn shape(&mut self, id: SlotId) {
fn shape(&mut self, resource: &mut TextRsc) {
let density = self.data.density;
let Self { data, entries, .. } = self;
let resource = entries
.get_mut(id)
.expect("text resource handle points at a released slot");
resource
.buffer
.shape(data, &resource.attrs, resource.width, density);
.shape(&mut self.data, &resource.attrs, resource.width, density);
}
fn render(
&mut self,
id: SlotId,
resource: &mut TextRsc,
width: Option<f32>,
owner: WidgetId,
textures: &mut Textures,
density: f32,
) -> (RenderedText, bool) {
let atlas_generation = self.data.atlas.generation();
let resource = self.entry_mut(id);
resource.owner = Some(owner);
if resource.width == width
&& resource.density == density
@@ -733,11 +722,7 @@ impl TextResources {
}
resource.width = width;
resource.density = density;
let Self { data, entries, .. } = self;
let resource = entries
.get_mut(id)
.expect("text resource handle points at a released slot");
let rendered = data.render(
let rendered = self.data.render(
&mut resource.buffer,
&resource.attrs,
width,
@@ -773,28 +758,30 @@ impl DerefMut for TextResources {
/// shared once per UI; constructing a handle does not allocate a resource of
/// its own.
pub struct TextHandle {
id: SlotId,
rsc: RscHandle<TextRsc>,
resources: Rc<RefCell<TextResources>>,
send: Sender<SlotId>,
}
impl TextHandle {
pub fn strong(&self) -> StrongRscId<TextRsc> {
self.rsc.strong()
}
pub fn weak(&self) -> WeakRscId<TextRsc> {
self.rsc.weak()
}
pub fn text(&self) -> Ref<'_, str> {
Ref::map(self.resources.borrow(), |resources| {
resources.entry(self.id).buffer.text()
})
Ref::map(self.rsc.get(), |resource| resource.buffer.text())
}
pub fn attrs(&self) -> Ref<'_, TextAttrs> {
Ref::map(self.resources.borrow(), |resources| {
&resources.entry(self.id).attrs
})
Ref::map(self.rsc.get(), |resource| &resource.attrs)
}
pub fn set_text(&mut self, text: impl Into<String>) -> bool {
let text = text.into();
let mut resources = self.resources.borrow_mut();
let resource = resources.entry_mut(self.id);
let mut resource = self.rsc.get_mut();
if resource.buffer.text() == text {
return false;
}
@@ -804,46 +791,45 @@ impl TextHandle {
}
pub fn edit_text<R>(&mut self, edit: impl FnOnce(&mut String) -> R) -> R {
let mut resources = self.resources.borrow_mut();
let resource = resources.entry_mut(self.id);
let mut resource = self.rsc.get_mut();
resource.rendered = None;
edit(resource.buffer.edit())
}
pub fn set_spans(&mut self, spans: Vec<SpanStyle>) {
let mut resources = self.resources.borrow_mut();
let resource = resources.entry_mut(self.id);
let mut resource = self.rsc.get_mut();
resource.buffer.set_spans(spans);
resource.rendered = None;
}
pub fn update_attrs<R>(&mut self, update: impl FnOnce(&mut TextAttrs) -> R) -> R {
let mut resources = self.resources.borrow_mut();
let resource = resources.entry_mut(self.id);
pub fn attrs_mut(&mut self) -> RefMut<'_, TextAttrs> {
let mut resource = self.rsc.get_mut();
resource.invalidate();
update(&mut resource.attrs)
RefMut::map(resource, |resource| &mut resource.attrs)
}
pub fn rendered(&self) -> Option<RenderedText> {
self.resources.borrow().entry(self.id).rendered.clone()
self.rsc.get().rendered.clone()
}
pub fn width(&self) -> Option<f32> {
self.resources.borrow().entry(self.id).width
self.rsc.get().width
}
pub fn with_layout<R>(&self, f: impl FnOnce(&Layout<PaintId>, &str) -> R) -> R {
let mut resources = self.resources.borrow_mut();
resources.shape(self.id);
let resource = resources.entry(self.id);
let mut resource = self.rsc.get_mut_shared();
resources.shape(&mut resource);
f(resource.buffer.layout(), resource.buffer.text())
}
pub fn layout(&self) -> Ref<'_, Layout<PaintId>> {
self.resources.borrow_mut().shape(self.id);
Ref::map(self.resources.borrow(), |resources| {
resources.entry(self.id).buffer.layout()
})
{
let mut resources = self.resources.borrow_mut();
let mut resource = self.rsc.get_mut_shared();
resources.shape(&mut resource);
}
Ref::map(self.rsc.get(), |resource| resource.buffer.layout())
}
pub fn render(
@@ -853,15 +839,9 @@ impl TextHandle {
textures: &mut Textures,
density: f32,
) -> (RenderedText, bool) {
self.resources
.borrow_mut()
.render(self.id, width, owner, textures, density)
}
}
impl Drop for TextHandle {
fn drop(&mut self) {
let _ = self.send.send(self.id);
let mut resources = self.resources.borrow_mut();
let mut resource = self.rsc.get_mut_shared();
resources.render(&mut resource, width, owner, textures, density)
}
}
@@ -886,11 +866,11 @@ mod tests {
TextBuffer::new("temporary"),
TextAttrs::default(),
);
assert_eq!(resources.borrow().entries.len(), 1);
assert_eq!(resources.borrow().entries.borrow().len(), 1);
drop(text);
resources.borrow_mut().free_released();
assert!(resources.borrow().entries.is_empty());
assert!(resources.borrow().entries.borrow().is_empty());
}
}