-
Notifications
You must be signed in to change notification settings - Fork 3
Performance-optimizations #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
-ctx actions (ctx.translate, ctx.rotate) instead of updating DOMMatrix. Note: this does not take into account skewing. further implementation will use `ctx.setTransform` and manually calculating the values instead.
`pointerEvents` are now opt-in to prevent unnecessary hitTests we still have to traverse the parent (and thus transform the ctx) in case a sibling has a `pointerEvents: true` fix group.hitTest to properly transform ctx before parentHood.
Controllers can now set internal flags. first usecase is `Drag()`: when dragging we set `context.flags.shouldHitTest` to false which short-circuits `shouldHitTest` in `createMouseEventHandler` This eliminates jank that came apparent in bigger scenes: `examples/Smileys` is able to drag now smoothly on ios/firefox (m2) with 600 instances.
and we perform a check before each `hitTest`. This basically short-circuits the `hitTest` once it encounters a hit eliminating jank while for example hovering. event-bubbling still is possible, but then it should be explicitly set to true inside an event-handler set by the user
|
Big performance-win by defaulting to
The jank (red lines) in clock-mode isn't noticeable, but still pretty epic auto-rendering out-performs clock-mode. Initially I envisioned to have auto-rendering as a first mode and if your scenes got too complex with too many animations and you needed more reliable rendering you would start using a clock to schedule rendering. But this graph showcases that simply throttling the render-effect is as performant, if not more performant. ¹ For clarity: in auto-render mode the render-function is executed inside a toggled createEffect. This way, whenever any prop changes that has an effect on rendering, and enough time has passed from last render, the render-function is automatically run. If nothing changed, nothing will re-render. |
uses the more optimal `ctx.translate` and `ctx.rotate` if no skew is present. If skew is present, it will `ctx.getTransform` twice mutate matrix1, mutate it, `ctx.setTransform(matrix1)` and then revert it back to `ctx.setTransform(matrix2)`. This is pretty wasteful, so there is room for improvement there.
Tokens with `style.pointerEvents: true` now register themselves. Currently it's only being used to set the flag `hasInteractiveTokens` which is being checked in `createMouseEventHandler`, later iterations could use this information to only perform hitTests on interactive tokens.
Is implemented with Interactive tokens are now being registered at top-level :
|
interactive tokens keep track of their transform-matrix and `setTransform/resetTransform` the ctx.
is now implemented: Interactive tokens keep track of their transform-matrix during rendering and set/reset the transform-matrix of the context during hitTest. This way we don't need to go all the way up the tree to calculate the correct transformation-matrix for the element during hitTest, which allows us to only check for the tokens which have More time is now spend for rendering instead of scripting: above run 30% of time was spend filling and stroking. We could probably improve on this pattern by specifying the type of mouse-events the token listens to, to further eliminate unnecessary hitTests ( We do get from time to time some |
A big amount of time (10%) was spent in getting styles in `Hover`. Caching the result up front in a mutated `style` variable brings the cost of accessing props.style down to 2%.
|
LGTM! |



Further performance-optimizations.
Test-scene is now Smileys, which has no blending-modes, but a more complicated structure (parent-siblings).
Optimizations include:
style.pointerEventsis now false, to prevent unnecessary hitTests. I was toying with the idea ofDragandHoverto set thestyleautomatically, but decided against it.DragandHoverwithstyle.pointerEvents === falseis now a no-op.internalContext.flags: controllers now can set flags. first use-case isDragandflags.shouldHitTest: while dragging it setsflag.shouldHitTestto false, preventing jank while dragging².createLazyMemoofcreateControlledProps: this result is now memo-ed instead.DragandHoverwere merging style-props with each update, this is now done during initialization.results experiment (ios m2 firefox, Smileys.tsx with 600 instances)
before
after
The part without red is when dragging, before and after are when hovering with the mouse. The hitTests clearly still have room for improvement.
¹ this does not account for skewing/scaling. ideally we should
setTransformand calculate the matrix-values ourselves. This could possibly negate all performance-benefits, but then we could default to translate/rotate in case scale/skew no-ops² It's a bit buggy atm: if you accidentally hover over something else just before you dragged that element will stay in that hover-style during the drag. Possible solution would be to introduce back the idea of having a
focused,hoveredandactiveelement and checking for that insideHover. This might even eliminate the idea offlagsin favor of checking for the active element with a selector.