Perf: Incremental culled shapes calculation. (#3411)

Reworks our culling logic:
- No longer show the gray rectangles for culled shapes. 
- Don't use `renderingBoundExpanded`, instead we now use
`viewportPageBounds`. I've removed `renderingBoundsExpanded`, but we
might want to deprecate it?
- There's now a incremental computation of non visible shapes, which are
shapes outside of `viewportPageBounds` and shapes that outside of their
parents' clipping bounds.
- There's also a new `getCulledShapes` function in `Editor`, which uses
the non visible shapes computation as a part of the culled shape
computation.
- Also moved some of the `getRenderingShapes` tests to newly created
`getCullingShapes` tests.

Feels much better on my old, 2017 ipad (first tab is this PR, second is
current prod, third is staging).


https://github.com/tldraw/tldraw/assets/2523721/327a7313-9273-4350-89a0-617a30fc01a2

### Change Type

<!--  Please select a 'Scope' label ️ -->

- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff

<!--  Please select a 'Type' label ️ -->

- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know


### Test Plan

1. Regular culling shapes tests. Pan / zoom around. Use minimap. Change
pages.

- [x] Unit Tests
- [ ] End to end tests

---------

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
Mitja Bezenšek 2024-04-10 12:29:11 +02:00 committed by GitHub
parent 2bbab1a790
commit 987b1ac0b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 357 additions and 401 deletions

View file

@ -21,7 +21,6 @@ import { toDomPrecision } from '../../primitives/utils'
import { debugFlags } from '../../utils/debug-flags'
import { setStyleProperty } from '../../utils/dom'
import { nearestMultiple } from '../../utils/nearestMultiple'
import { CulledShapes } from '../CulledShapes'
import { GeometryDebuggingView } from '../GeometryDebuggingView'
import { LiveCollaborators } from '../LiveCollaborators'
import { Shape } from '../Shape'
@ -125,9 +124,6 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
<Background />
</div>
)}
<div className="tl-culled-shapes">
<CulledShapes />
</div>
<div
ref={rCanvas}
draggable={false}
@ -388,6 +384,30 @@ function ShapesWithSVGs() {
</>
)
}
function ReflowIfNeeded() {
const editor = useEditor()
const culledShapesRef = useRef<Set<TLShapeId>>(new Set())
useQuickReactor(
'reflow for culled shapes',
() => {
const culledShapes = editor.getCulledShapes()
if (
culledShapesRef.current.size === culledShapes.size &&
[...culledShapes].every((id) => culledShapesRef.current.has(id))
)
return
culledShapesRef.current = culledShapes
const canvas = document.getElementsByClassName('tl-canvas')
if (canvas.length === 0) return
// This causes a reflow
// https://gist.github.com/paulirish/5d52fb081b3570c81e3a
const _height = (canvas[0] as HTMLDivElement).offsetHeight
},
[editor]
)
return null
}
function ShapesToDisplay() {
const editor = useEditor()
@ -408,6 +428,7 @@ function ShapesToDisplay() {
{renderingShapes.map((result) => (
<Shape key={result.id + '_shape'} {...result} dprMultiple={dprMultiple} />
))}
{editor.environment.isSafari && <ReflowIfNeeded />}
</>
)
}