Jetpack Compose Desktop rendering performances
Tags: kotlin, skia
Published: 2021-06-09 12:40:26

As Jetpack Compose is gaining traction, we plan to offer a specific implementation for our [Kotlin Charting Library, Charts.kt](https://charts-kt.io/). 

Before starting to code it, we have to check if it’s a viable solution and if the rendering performances are already good enough. 

This small post shows the resulting tests.

## What to test?

The graphic needs for data visualization are relatively simple. You can do almost anything you want with rectangles, circles, and paths. Generally, rectangles and circles are directly available in the graphics API. You draw more complex shapes with paths.



So to test the rendering performances, we created a simple application that produces random particles to visualize on a canvas. Using different symbols and counts of particles, we can evaluate the rendering performance of Jetpack Compose Desktop.

We also implemented a JavaFX version for comparison.

## Jetpack Compose Desktop

The code is available on GitHub: https://github.com/gzoritchak/compose-rendering

The test implementation uses the 0.4.0 version of JetPack Compose. 

We focused our code on the rendering performances and not on the respect of Compose implementation patterns. 

In particular, we limit the memory allocations. The particles are mutable classes that contain their position and speed. They are instantiated during startup and updated at each frame. 

We then propose 3 different rendering of the particle’s position: a square, a circle and a diamond. The diamond is built through a path. 

```kotlin
val particle = particles[id]
val x = xScale(particle.x).toFloat()
val y = yScale(particle.y).toFloat()
val color = Colors.Web.aliceblue.toColor()
when (renderingWith) {
    RenderingWith.Square -> drawRect(
        color = color,
        Offset(x, y),
        Size(10f, 10f)
    )
    RenderingWith.Circle -> drawCircle(
        color = color,
        radius = 5f,
        center = Offset(x, y)
    )
    RenderingWith.Diamond -> drawPath(Path().apply {
        moveTo(x, y + 5)
        lineTo(x + 5, y)
        lineTo(x, y - 5)
        lineTo(x - 5, y)
        close()
    }, color)
}
```

See the resulting performances on my MacBook Pro (late 2013 2,3 GHz Intel Core i7).

{% youtube h-mtBOSkuRI%}

Up to 10 000 particles, the rendering performs at 60 FPS using squares and circles. It then fell to approximately 30 FPS for 30 000 particles and 8-9 FPS for 100 000 particles. The first decrease with paths (diamonds) appears at 10 000 particles with a not stable 30 FPS. We can also observe some freeze.

| Compose | 3000   | 10 000 | 30 000 |100 000 |
| ------- |:------:| ------:| ------:| ------:|
| Square  | 60 FPS | 60 FPS | 28 FPS |  8 FPS |
| Circle  | 60 FPS | 60 FPS | 31 FPS |  9 FPS |
| Diamond | 60 FPS | 30 FPS | 10 FPS |  2 FPS |

Now let’s compare these performances with a settled framework. 

## JavaFX/TornadoFX implementation

We implemented a JavaFX version of this test application to have a comparison point.

The code is available on this [GitHub project](https://github.com/gzoritchak/JavaFx-rendering).

We render the particles through the Data2viz library, which is the current implementation for our charting library. Under the hood, the Data2viz library uses a JavaFX canvas.

Here is a screencast of the results using the same hardware:

{% youtube jX0cckddYWY %}

We can observe that the rendering speed is double for squares and circles. The difference is weaker when we use paths.

| JavaFX  | 3000   | 10 000 | 30 000 |100 000 |
| ------- |:------:| ------:|-------:|-------:|
| Square  | 60 FPS | 60 FPS | 60 FPS | 20 FPS |
| Circle  | 60 FPS | 60 FPS | 60 FPS | 20 FPS |
| Diamond | 60 FPS | 37 FPS | 13 FPS |  3 FPS |

## Conclusion


Even if the performances are not as good as their JavaFX counterpart, they are already good enough for many visualization use cases. 

Jetbrains builds Jetpack Compose Desktop on solid foundations. The works they are doing on integrating Skia in a Kotlin context is also promising for new usages (Swing, Headless).