Fix scroll slowdown at depth: bound the render window with word wrap
With word wrap on, VisibleByteRange mapped the viewport TOP from visual to logical line space (WrapIndex.LineForVisual) but left the viewport BOTTOM as the raw visual-line number and used it as a LOGICAL index. The window therefore fetched (viewport lines + every wrapped line above the viewport) of real text: an over-fetch that grew without bound with scroll depth. Measured on a 2.9 MB file with wrap on: the shaped window was ~3.7 KB at 300 KB depth but ~17.4 KB at 2.8 MB depth, costing 12 ms of shaping and 40 ms of frame draw per frame there — scrolling visibly degraded the further down the file you got. Map the bottom through LineForVisual exactly like the top: the logical line containing the viewport's bottom visual line is the correct end, and since each logical line yields at least one visual line the window still always covers the viewport. The fetch is now viewport-bounded at every depth (window flat at ~1 KB across 300 KB..2.8 MB in the test file; deep frames drop from 40 ms to <4 ms). Also drop the instrumentation added while diagnosing.
This commit is contained in:
parent
f54ca2f81a
commit
36b6a86873
|
|
@ -452,10 +452,12 @@ func (cb *ChunkedBuffer) VisibleByteRange(scrollOffset ui.Dp, byteOffset int, vi
|
|||
// released). That is the root cause of the ~1GB "Unknown" memory on large
|
||||
// files.
|
||||
//
|
||||
// Word wrap does not require a separate path: each real line produces at
|
||||
// least one visual line, so shaping viewportHeight/lineHeight real lines
|
||||
// always yields at least as many visual lines as fit in the viewport. The
|
||||
// extra wrapped lines are simply clipped by the renderer.
|
||||
// Word wrap does not require a separate path: the viewport top and bottom
|
||||
// are each expressed in VISUAL-line space and mapped to the logical lines
|
||||
// that contain them (WrapIndex.LineForVisual). Because each logical line
|
||||
// produces at least one visual line, that logical range always covers the
|
||||
// viewport; the fetch is viewport-bounded no matter how many wraps lie
|
||||
// above it, and any spill past the bottom edge is clipped by the renderer.
|
||||
lineH := lineHeight
|
||||
if lineH <= 0 {
|
||||
lineH = EffectiveLineHeight()
|
||||
|
|
@ -564,10 +566,18 @@ func (cb *ChunkedBuffer) visibleByteRangePrecise(scrollOffset ui.Dp, viewportHei
|
|||
// (the legacy no-wrap behavior).
|
||||
v0, _ := scrollDecompose(scrollOffset, lineHeight)
|
||||
startLine = int(v0)
|
||||
endLine := int(math.Ceil(float64(scrollOffset+viewportHeight) / float64(lineHeight)))
|
||||
if w := cb.WrapIndex; w != nil {
|
||||
startLine = w.LineForVisual(int32(v0))
|
||||
// endLine is still in VISUAL space: it is the visual line at the
|
||||
// viewport bottom, so map it to the logical line that contains it,
|
||||
// exactly like the top. Using the raw visual number as a logical
|
||||
// index over-fetched the window by every wrapped line above the
|
||||
// viewport — an over-fetch that grows without bound with scroll
|
||||
// depth, so shaping/drawing (and therefore scroll responsiveness)
|
||||
// degraded the further down the file you were.
|
||||
endLine = w.LineForVisual(int32(endLine))
|
||||
}
|
||||
endLine := int(math.Ceil(float64(scrollOffset+viewportHeight) / float64(lineHeight)))
|
||||
|
||||
if startLine < 0 {
|
||||
startLine = 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user