Skip to content

Sheets: scroll & arrow-key selection cost is DPR-bound (canvas re-raster); request render pixelRatio option #7197

Description

@neooleg

Summary

On sheets with a modest dataset (~40 rows × 20 cols, padded grid 240×30), continuous wheel scrolling and arrow-key selection moves spend most of their budget re-rasterizing the canvas. The cost scales almost linearly with devicePixelRatio, and profiling shows text/style raster as the hot path — suggesting the viewport is re-drawn wholesale per frame / per selection move. There is currently no public knob to trade render resolution for performance (IUniverEngineRenderConfig is empty in 0.25.0).

Environment

  • @univerjs/presets 0.25.0 (also checked 0.25.1 — release notes say Pro-only fixes), presets: sheets-core, filter, sort, find-replace, conditional-formatting, data-validation, hyper-link, note
  • Chrome 148, macOS 12, Intel MacBook, retina display (devicePixelRatio 2)
  • Viewports tested: 368×130 CSS px (docked panel) and 1404×547 CSS px (full screen)

Measurements

Synthetic wheel storm (~60 events/s for 8 s) and arrow-key walk (36 presses at ~8 Hz), measured with rAF sampling, the Event Timing API, and CDP Performance.getMetrics deltas:

Scenario Main-thread scripting / 8 s scroll Arrow-press p95 (Event Timing)
368×130 viewport, DPR 2 ~2,440 ms ~104 ms (20/36 presses ≥16 ms)
1404×547 viewport, DPR 2 ~2,830 ms ~112 ms (28/36 ≥16 ms)
same, devicePixelRatio overridden to 1.5 before boot −53…57% (≈1,150–1,220 ms) ~56 ms

Attribution:

  • DevTools trace (8 s docked scroll): ~2.57 s of 2.95 s scripting self-time inside the Univer bundle, dominated by FunctionCall under the wheel/rAF pipeline.
  • 5 kHz CPU profile (unminified build, 6.4 s scroll storm) — top self-time frames after idle/program: fillText, canvas font getter, hexToColor / toHexString, drawImage, getBoundingClientRect. Filter/sort code contributes ~0 ms (their presence or absence doesn't change the profile).

Happy to attach the full .cpuprofile and trace JSON.

Requests

  1. Expose a render pixelRatio / render-scale option (engine-render reads window.devicePixelRatio internally in several places — getDevicePixelRatio() at init and direct reads on resize/buffer paths — so hosts today can only override the global getter, which is a blunt instrument). A per-instance cap would let hosts trade a little sharpness for a ~2× main-thread win on weak-GPU / hi-DPI machines.
  2. Consider caching/partial redraw on the scroll and selection-move paths. The DPR sensitivity plus the fillText/font/color-conversion dominance suggests visible cells are re-rasterized wholesale on every frame and every selection move; a row/cell raster cache or dirty-region redraw would likely help all hosts without any API change.

Thanks for Univer — the 0.25.0 scroll improvements are noticeable; this is the next bottleneck we hit in production profiling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions