Skip to content

Kietyo/TestComposeRendering

Repository files navigation

This is a Kotlin Multiplatform project targeting Web, Desktop (JVM), Server.

  • /composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:

    • commonMain is for code that’s common for all targets.
    • Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
  • /server is for the Ktor server application.

  • /shared is for the code that will be shared between all targets in the project. The most important subfolder is commonMain. If preferred, you can add code to the platform-specific folders here too.

Build and Run Desktop (JVM) Application

To build and run the development version of the desktop app, use the run configuration from the run widget in your IDE’s toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:run
  • on Windows
    .\gradlew.bat :composeApp:run

Build and Run Server

To build and run the development version of the server, use the run configuration from the run widget in your IDE’s toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :server:run
  • on Windows
    .\gradlew.bat :server:run

Build and Run Web Application

To build and run the development version of the web app, use the run configuration from the run widget in your IDE's toolbar or run it directly from the terminal:

  • for the Wasm target (faster, modern browsers):
    • on macOS/Linux
      ./gradlew :composeApp:wasmJsBrowserDevelopmentRun
    • on Windows
      .\gradlew.bat :composeApp:wasmJsBrowserDevelopmentRun
  • for the JS target (slower, supports older browsers):
    • on macOS/Linux
      ./gradlew :composeApp:jsBrowserDevelopmentRun
    • on Windows
      .\gradlew.bat :composeApp:jsBrowserDevelopmentRun

Learn more about Kotlin Multiplatform, Compose Multiplatform, Kotlin/Wasm

We would appreciate your feedback on Compose/Web and Kotlin/Wasm in the public Slack channel #compose-web. If you face any issues, please report them on YouTrack.


Render V1: 30K circles, 30 fps

Render V2: 30K circles, ~40 fps

Render V3: 30K circles, ~50 fps (Optimized with drawPoints and simulation moved to LaunchedEffect)

Render V4: 30K circles, ~60 fps (Optimized with drawRawPoints and minimal data overhead)

  • 50K circles, ~44 fps
  • 100K circles, ~21 fps

Render V5: 30K circles, ~70+ fps (Optimized for cache locality with interleaved arrays and reduced Compose state overhead)

  • 50K circles, ~48 fps
  • 100k circles, 24 fps

Render V6:

  • 100k circles, ~24 fps

Description of various render versions:

V1 — Baseline: separate arrays + per-circle drawCircle What it is: The simplest, most straightforward implementation. How it works: Stores X/Y and velocities in four separate FloatArrays and draws each circle individually with drawCircle. Tradeoffs: Easy to understand, but tends to be slower at high counts due to per-circle draw calls and scattered memory access.

V2 — Packed data: single array (AoS) + per-circle drawCircle What it is: Same visual approach, but with a more cache-friendly data structure. How it works: Packs each circle’s (x, y, vx, vy) into one FloatArray with a stride of 4. Still draws each circle with drawCircle. Tradeoffs: Reduces the number of arrays and can improve memory locality, but still pays the high cost of one draw call per circle.

V3 — Batch rendering: drawPoints + simulation separated from drawing What it is: A rendering-optimized version using batched point drawing. How it works: Draws all circles as points via drawPoints (fast path for tiny circles). Simulation updates run in the frame loop; drawing rebuilds a list of Offsets each frame. Tradeoffs: Rendering is much faster than per-circle drawCircle, but creating a MutableList every frame adds allocation/GC overhead and can become the new bottleneck.

V4 — Raw points: drawRawPoints + split arrays for positions/velocities What it is: Eliminates per-frame Offset allocations and uses a lower-level canvas API. How it works: Keeps circlePositions and circleVelocities as FloatArrays and renders via drawContext.canvas.drawRawPoints(...) with a reusable Paint. Tradeoffs: Much less allocation and faster drawing. Logic is a bit more “systems-level” (manual indexing), but performance is the goal here.

V5 — Refined data model: packed simulation + separate positions buffer for drawing What it is: A “tight loop” version designed to minimize Compose/state overhead while keeping rendering fast. How it works: Uses a packed FloatArray for simulation (x,y,vx,vy) and a separate FloatArray for draw positions (x,y) only. Updates both arrays in one loop, and triggers redraws via a tick. Tradeoffs: Very efficient: minimal allocations, good cache locality, and fast rendering. Slightly more complex bookkeeping (two buffers), but it’s a common high-performance pattern.

V6 — Parallel simulation: multi-core update with coroutines + raw point rendering What it is: Attempts to scale simulation across CPU cores. How it works: Splits circles into chunks and updates them in parallel using coroutines on Dispatchers.Default, then syncs to the display frame. Rendering is still drawRawPoints. Tradeoffs: Can help when simulation math is the bottleneck and you have multiple cores. However, on some targets (especially web/Wasm), true parallelism may be limited, and coroutine overhead/coordination can eat into gains.


Gradle JVM: ms-21 (can download using intellij)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published