Refactor TextLayout to use Rc<RefCell> instead of Arc<Mutex> for improved performance (#27177)

Since `TextLayout` is not shared by multiple threads, changing it to
`Rc<RefCell<T>>` should improve performance.

I also found several codes with the same problem. If you think this
change is beneficial, I will continue to improve it in the subsequent
PR. 🙂

Release Notes:

- N/A
This commit is contained in:
Sunli 2025-03-22 03:06:22 +08:00 committed by GitHub
parent 6143da95fc
commit 1db621dc50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -6,7 +6,6 @@ use crate::{
WrappedLineLayout,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec;
use std::{
cell::{Cell, RefCell},
@ -277,7 +276,7 @@ impl IntoElement for StyledText {
/// The Layout for TextElement. This can be used to map indices to pixels and vice versa.
#[derive(Default, Clone)]
pub struct TextLayout(Arc<Mutex<Option<TextLayoutInner>>>);
pub struct TextLayout(Rc<RefCell<Option<TextLayoutInner>>>);
struct TextLayoutInner {
lines: SmallVec<[WrappedLine; 1]>,
@ -288,10 +287,6 @@ struct TextLayoutInner {
}
impl TextLayout {
fn lock(&self) -> MutexGuard<Option<TextLayoutInner>> {
self.0.lock()
}
fn layout(
&self,
text: SharedString,
@ -341,7 +336,7 @@ impl TextLayout {
(None, None)
};
if let Some(text_layout) = element_state.0.lock().as_ref() {
if let Some(text_layout) = element_state.0.borrow().as_ref() {
if text_layout.size.is_some()
&& (wrap_width.is_none() || wrap_width == text_layout.wrap_width)
{
@ -367,7 +362,7 @@ impl TextLayout {
)
.log_err()
else {
element_state.lock().replace(TextLayoutInner {
element_state.0.borrow_mut().replace(TextLayoutInner {
lines: Default::default(),
line_height,
wrap_width,
@ -384,7 +379,7 @@ impl TextLayout {
size.width = size.width.max(line_size.width).ceil();
}
element_state.lock().replace(TextLayoutInner {
element_state.0.borrow_mut().replace(TextLayoutInner {
lines,
line_height,
wrap_width,
@ -400,7 +395,7 @@ impl TextLayout {
}
fn prepaint(&self, bounds: Bounds<Pixels>, text: &str) {
let mut element_state = self.lock();
let mut element_state = self.0.borrow_mut();
let element_state = element_state
.as_mut()
.ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
@ -409,7 +404,7 @@ impl TextLayout {
}
fn paint(&self, text: &str, window: &mut Window, cx: &mut App) {
let element_state = self.lock();
let element_state = self.0.borrow();
let element_state = element_state
.as_ref()
.ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
@ -438,7 +433,7 @@ impl TextLayout {
/// Get the byte index into the input of the pixel position.
pub fn index_for_position(&self, mut position: Point<Pixels>) -> Result<usize, usize> {
let element_state = self.lock();
let element_state = self.0.borrow();
let element_state = element_state
.as_ref()
.expect("measurement has not been performed");
@ -472,7 +467,7 @@ impl TextLayout {
/// Get the pixel position for the given byte index.
pub fn position_for_index(&self, index: usize) -> Option<Point<Pixels>> {
let element_state = self.lock();
let element_state = self.0.borrow();
let element_state = element_state
.as_ref()
.expect("measurement has not been performed");
@ -503,7 +498,7 @@ impl TextLayout {
/// Retrieve the layout for the line containing the given byte index.
pub fn line_layout_for_index(&self, index: usize) -> Option<Arc<WrappedLineLayout>> {
let element_state = self.lock();
let element_state = self.0.borrow();
let element_state = element_state
.as_ref()
.expect("measurement has not been performed");
@ -533,18 +528,18 @@ impl TextLayout {
/// The bounds of this layout.
pub fn bounds(&self) -> Bounds<Pixels> {
self.0.lock().as_ref().unwrap().bounds.unwrap()
self.0.borrow().as_ref().unwrap().bounds.unwrap()
}
/// The line height for this layout.
pub fn line_height(&self) -> Pixels {
self.0.lock().as_ref().unwrap().line_height
self.0.borrow().as_ref().unwrap().line_height
}
/// The text for this layout.
pub fn text(&self) -> String {
self.0
.lock()
.borrow()
.as_ref()
.unwrap()
.lines