Skip to content

Introduces several caching of objects across graphics Skiko code#3117

Open
ApoloApps wants to merge 9 commits into
JetBrains:jb-mainfrom
ApoloApps:followupMatrixSkiko
Open

Introduces several caching of objects across graphics Skiko code#3117
ApoloApps wants to merge 9 commits into
JetBrains:jb-mainfrom
ApoloApps:followupMatrixSkiko

Conversation

@ApoloApps

@ApoloApps ApoloApps commented Jun 11, 2026

Copy link
Copy Markdown
  1. Reuse Matrix33 in SkiaBackedPath (same behaviour as Android)
  2. Cache FloatArray for addRoundRect methods in Path and GraphicsLayer (Skiko classes, same as Android)
  3. Added CanvasHolder (same as Android) which caches the SkiaBackedCanvas object across frames to avoid its allocationon every frame (avoids substantial amount of allocations and aligns with Android's behaviour too)
  4. SyntheticEventSender optimizations by avoiding iterators, boxing and overall colection copies (moved to another PR Separated PR 3117 changes that have nothing to do with graphics #3164)

Fixes https://youtrack.jetbrains.com/issue/CMP-9463/Web-asComposeCanvas-method-returns-object-allocated-on-each-rendered-frame (for all targets, not only web)

Release Notes

N/A

…for indexed loop instead of generic Collection which allocates the iterator)
…ct on each frame (it reuses the same via CanvasHolder.drawInto). This aligns the behaviour with Android's. Uses the exact same pattern
@ApoloApps ApoloApps marked this pull request as ready for review June 12, 2026 10:18

private typealias PointerToPositionMap = LongLongMap

private typealias PointerIdSet = MutableLongSet

@ApoloApps ApoloApps Jun 15, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-boxing collection is way faster that HashSet especially in terms of contains operator, which is widely used across the file
WasmJs

Operation Size HashSet (ops/μs) LongSet (ops/μs) Improvement (%)
Add 16 4.803 5.266 +9.64%
  256 0.381 0.401 +5.25%
  4096 0.023 0.025 +8.70%
Contains 16 7.199 12.788 +77.64%
  256 0.385 0.861 +123.64%
  4096 0.026 0.048 +84.62%
Iterate 16 17.634 39.695 +125.11%
  256 2.001 2.835 +41.68%
  4096 0.128 0.172 +34.38%
Remove 16 2.806 3.177 +13.22%
  256 0.174 0.251 +44.25%
  4096 0.013 0.016 +23.08%

@ApoloApps

Copy link
Copy Markdown
Author

@MatkovIvan Ivan Matkov (MatkovIvan) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

My main concern here is the shape of the newly introduced public API - it doesn't seem that it should be exposed like that for wide audience with full compatibility guarantees

* Holder class that is used to issue scoped calls to a [Canvas] from the framework equivalent
* canvas without having to allocate an object on each draw call
*/
class CanvasHolder {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: a new public-stable API that we'll have to maintain forever

internal val internalSkiaCanvas: SkCanvas,
) : Canvas {
@PublishedApi
internal class SkiaBackedCanvas : Canvas {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't supposed to be exposed. Again, requirement for binary compatibility in a place where we're not ready to do it

@ApoloApps

Copy link
Copy Markdown
Author

I'll wait for the #3126 changes

@ApoloApps

Copy link
Copy Markdown
Author

Separated non-graphics related changes to this PR #3164 to unblock merging some parts of this PR. After other PR merged I will be rebasing (as the other PR contains commits of this one)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants