Revert "Optimize editor rendering when clipped by parent containers" (#45011)
This reverts commit f576ec7a15 (#44995).
The optimization introduced a regression that causes the main thread to
hang for **100+ seconds** in certain scenarios, requiring a force quit
to recover.
## Analysis from spindump
When a large `AutoHeight` editor is displayed inside a `List` (e.g.,
Agent Panel thread view), the clipping calculation can produce invalid
row ranges:
1. `visible_bounds` from `window.content_mask().bounds` represents the
window's content mask, not the intersection with the editor
2. When the editor is partially scrolled out of view,
`clipped_top_in_lines` becomes extremely large
3. This causes `start_row` to be computed as an astronomically high
value
4. `blocks_in_range(start_row..end_row)` then spends excessive time in
`Cursor::search_forward` iterating through the block tree
The spindump showed **~46% of samples** (459/1001 over 10+ seconds)
stuck in `BlockSnapshot::blocks_in_range()`, specifically in cursor
iteration.
### Heaviest stack trace
```
EditorElement::prepaint
└─ blocks_in_range + 236
└─ Cursor::search_forward (459 samples)
```
## Symptoms
- Main thread unresponsive for 33-113 seconds before sampling even began
- UI completely frozen
- High CPU usage on main thread (10+ seconds of CPU time in the sample)
- Force quit required to recover
## Path forward
The original optimization goal (reducing line layout work for clipped
editors) is valid, but the implementation needs to:
1. Correctly calculate the **intersection** of editor bounds with the
visible viewport
2. Ensure row calculations stay within valid ranges (clamped to
`max_row`)
3. Handle edge cases where the editor is completely outside the visible
bounds
Release Notes:
- Fixed a hang that could occur when viewing large diffs in the Agent
Panel
This commit is contained in:
1 parent
4b2c0074f0
commit
48580b521c
2 files changed
+7
-18
No files matched your search
@@ -9164,15 +9164,6 @@ impl Element for EditorElement {
|
||||
let height_in_lines = f64::from(bounds.size.height / line_height);
|
||||
let max_row = snapshot.max_point().row().as_f64();
|
||||
|
||||
// Calculate how much of the editor is clipped by parent containers (e.g., List).
|
||||
// This allows us to only render lines that are actually visible, which is
|
||||
// critical for performance when large AutoHeight editors are inside Lists.
|
||||
let visible_bounds = window.content_mask().bounds;
|
||||
let clipped_top = (visible_bounds.origin.y - bounds.origin.y).max(px(0.));
|
||||
let clipped_top_in_lines = f64::from(clipped_top / line_height);
|
||||
let visible_height_in_lines =
|
||||
f64::from(visible_bounds.size.height / line_height);
|
||||
|
||||
// The max scroll position for the top of the window
|
||||
let max_scroll_top = if matches!(
|
||||
snapshot.mode,
|
||||
@@ -9229,14 +9220,10 @@ impl Element for EditorElement {
|
||||
let mut scroll_position = snapshot.scroll_position();
|
||||
// The scroll position is a fractional point, the whole number of which represents
|
||||
// the top of the window in terms of display rows.
|
||||
// We add clipped_top_in_lines to skip rows that are clipped by parent containers,
|
||||
// but we don't modify scroll_position itself since the parent handles positioning.
|
||||
let start_row =
|
||||
DisplayRow((scroll_position.y + clipped_top_in_lines).floor() as u32);
|
||||
let start_row = DisplayRow(scroll_position.y as u32);
|
||||
let max_row = snapshot.max_point().row();
|
||||
let end_row = cmp::min(
|
||||
(scroll_position.y + clipped_top_in_lines + visible_height_in_lines).ceil()
|
||||
as u32,
|
||||
(scroll_position.y + height_in_lines).ceil() as u32,
|
||||
max_row.next_row().0,
|
||||
);
|
||||
let end_row = DisplayRow(end_row);
|
||||
|
||||
@@ -176,9 +176,11 @@ pub fn block_content_for_tests(
|
||||
}
|
||||
|
||||
pub fn editor_content_with_blocks(editor: &Entity<Editor>, cx: &mut VisualTestContext) -> String {
|
||||
let draw_size = size(px(3000.0), px(3000.0));
|
||||
cx.simulate_resize(draw_size);
|
||||
cx.draw(gpui::Point::default(), draw_size, |_, _| editor.clone());
|
||||
cx.draw(
|
||||
gpui::Point::default(),
|
||||
size(px(3000.0), px(3000.0)),
|
||||
|_, _| editor.clone(),
|
||||
);
|
||||
let (snapshot, mut lines, blocks) = editor.update_in(cx, |editor, window, cx| {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let text = editor.display_text(cx);
|
||||
|
||||
Reference in new issue
Block a user