RenderNode in Jetpack Compose (and Views)

(Thanks @romainguy for clearing up these details 🤩)

RenderNodes are a fundamental part of how Android's UI rendering works, both in traditional Views and in Jetpack Compose. Understanding how they work can help you write more performant UI code and make better architectural decisions.


In this post, we'll explore what RenderNodes are, how they optimize rendering performance, and why they're so important for efficient UI rendering on Android.

What Are RenderNodes?

RenderNodes visualization showing how drawing commands are cached and reused

RenderNodes are a list of drawing commands with some other properties like transforms (e.g: translation). These commands can be used to issue GPU commands by the internal renderer. They are not GPU commands on their own.

When a chunk of UI (View or graphicsLayer) is associated with a RenderNode, the UI toolkit doesn't need to reexecute the code that generates those drawing commands, but can instead reuse the entire list of commands ✨

This reuse mechanism is one of the key performance optimizations in Android's rendering system.

RenderNodes in graphicsLayer

An example of this optimization can be found in graphicsLayer 🎨

Using a graphicsLayer means a RenderNode is used to capture the drawing commands of the involved items. This is no different than what Views did. The key benefit is that once the drawing commands are captured in a RenderNode, they can be reused without regenerating the commands from scratch. Let's see an example of this in LazyLists.

Performance Benefits in LazyLists

In LazyLists for example, when scrolling, all the UI toolkit has to do is change the Y translation transform of each RenderNode, so no Java/Kotlin code has to run again. Just offset the already cached block of commands.

This is a huge performance win because:

  • No recomposition needs to happen during scrolling (for every pixel change 😱)
  • No drawing commands need to be regenerated
  • Only the transform properties need to be updated (translation Y)
  • The cached command blocks are simply repositioned

This optimization is one of the reasons why LazyLists can scroll so smoothly even with complex item layouts (on top of recycling, of course).

CPU vs GPU Performance

RenderNodes exist to save CPU time, not GPU 🧐

The GPU will do the same amount of work, but the CPU will do less since the drawing commands do not need to be generated again, but the whole block (list of commands) is reused.

This is an important distinction because:

  • GPU workload remains the same - it still needs to draw the same pixels
  • CPU workload is reduced - it doesn't need to regenerate drawing commands
  • The bottleneck in UI rendering is often on the CPU side
  • Reducing CPU work frees up resources for other tasks

Take the online course and join the exclusive community

Attendee avatarAttendee avatarAttendee avatarAttendee avatarAttendee avatarAttendee avatar

Master Jetpack Compose and learn how to work efficiently with it. Enjoy the perfect mix of theory and exercises with the best trainer. Join a community of 500+ devs.

★★★★★
Jorge Castillo
Created and delivered by Jorge Castillo, Google Developer Expert for Android and Kotlin
Trusted by top companies

Offscreen graphicsLayer and Textures

Now on top of this, offscreen graphicsLayer are an extra level of caching, where all the commands of the RenderNode are issued to the GPU to capture the result into a texture.

On subsequent draws, the only command sent to the GPU is "draw this texture", which is fast and efficient.

Trade-offs of Texture Caching

This process costs RAM, bandwidth, and if the layer changes frequently you may end up doing the work twice (regenerate the texture, then copy the texture on screen).

🔔 And textures obviously won't look good if zoomed with very big or very small scale factors for instance.

That said, note that sometimes using a texture is necessary (overlapping alphas and various other effects).

When to Use RenderNodes

Understanding RenderNodes helps you make better decisions about when to use optimizations like graphicsLayer:

Good Use Cases:

  • Content that changes position or scale frequently (like during animations). (Via graphicsLayer).
  • Complex layouts that are expensive to recompose
  • Items in scrolling lists
  • UI elements with expensive drawing operations

Final Thoughts

RenderNodes are a powerful optimization in Android's rendering system that help reduce CPU work by caching drawing commands. They work the same way in both traditional Views and Jetpack Compose.

Key takeaways:

  • RenderNodes cache drawing commands to avoid regenerating them
  • They optimize CPU performance, not GPU performance
  • They're especially effective for scrolling and animations
  • Texture caching provides additional benefits but comes with trade-offs
  • Understanding them helps you make better performance decisions

The next time you're optimizing your Compose UI, remember that RenderNodes are working behind the scenes to make your app more efficient!

Take the online course and join the exclusive community

Attendee avatarAttendee avatarAttendee avatarAttendee avatarAttendee avatarAttendee avatar

Master Jetpack Compose and learn how to work efficiently with it. Enjoy the perfect mix of theory and exercises with the best trainer. Join a community of 500+ devs.

★★★★★
Jorge Castillo
Created and delivered by Jorge Castillo, Google Developer Expert for Android and Kotlin
Trusted by top companies