You.i Engine One Performance: Manipulating the Scene Tree
By: John Cassidy
Tags: Mobile App Development, JavaScript, React, Development, React Native

The Scene Tree

All JSX Components written in a React Native application are ultimately translated to platform level nodes understood by the underlying rendering engine. The representation of these nodes can be described as a Scene Tree. It contains the relationships and contextual information needed by the engine to render the application.

This article will describe how to minimize the changes on the scene tree, which may be expensive in certain situations on low end devices, in order to provide the best user experience possible while managing memory constraints.

Creation and Destruction on a whim

A common pattern when building an application in either ReactJS or React Native is the use of Conditional Rendering to quickly change the stateful representation of a screen.

This allows for a render pass to completely change what is on screen with the change of a single state.

Set and Reset of Image via Conditional Rendering

In this example, the <Image /> is being destroyed or created on each pass depending on the state of the showImage variable. When a change like this occurs, the Scene Tree requires an update to redraw itself with the absence or inclusion of this node.

Changing Visual state without Creation or Destruction

Each component in JSX has a Shadow View that links it to a platform node that represents it at the engine rendering level. This node is what is represented in the Scene Tree. In the case of an <Image /> node, for the You.i Engine One platform, it is a CYIImageView .

If we reach into the platform with a Native Module, we can manipulate this counterpart view in the scene tree and cause a change in behaviour without requiring the creation or destruction of an element. (In this example I will also set opacity on the node to show a visual differentiation).

Replacing the conditional rendering logic with some that relies on working with references to items are created once, we can utilize the Native modules and achieve the same result with less cost.

While this is possible with iOS and Android Native Modules on Facebook React Native, the important distinction is that the Native Module to manipulate the CYIImageView is effective on every single platform supported by You.i Engine One.

Set and Reset Image (and opacity) via manipulation of Engine Scene Tree Node

This example demonstrates a small sample of what can be achieved with this approach. Effectively, all functionality of the underlying node is made available to you when you access the counterpart engine node. While this example demonstrates managing the state of an image, and resetting it to the initial state of the node, it’s possible to use this to unlock functionality beyond the capabilities of Facebook React Native.

Performance & Memory Management for Low End Devices

While the above is a simple isolated example, where this can come in handy is when you have a design that calls for a very large number of assets to be displayed in a list, each having an image attached to them.

For an application that has 25 lanes with 30 assets each, this can quickly overwhelm a low end device.

There are multiple approaches using core Facebook React Native to manage the display of these images and prevent images not on screen from attempting to download, but they typically rely on the notion that if you don’t want it to be present then conditionally make it go away.

windowSize

The most notable is windowSize on a virtualized list. This is an indicator of the streaming range of a list, how many list items (in number of screens) to keep created at any given time to enhance the user experience as they scroll through. Lowering the windowSize property can provide the benefits of (1) lower memory consumption, especially if images are involved, and (2) quicker load times as there are less nodes to create.

The issue with windowSize is that it works in absolutes — the list item is not created, and then it is, and then it isn’t. If you set your windowSize to be too small, the user may have a poor experience as the quickly scrolling through items can result in momentary blank spaces as items load in taking the user away from their immersive experience.

onViewableItemsChanged

By tracking the indices that are visible at any given moment, it is possible to set the state of individual list items informing them if they are in the viewable range or not. This is configurable with viewabilityConfig but can also be controlled manually in the onViewableItemsChanged callback if you prefer.

A wrapper class can provide this added contextual information to the renderItem method for use in the list.

With the wrapper class, the list items when rendering will be aware of it they are in the visible range as renderItem props can be destructured as ( {item, index, show}) and the item act accordingly.

To best utilize this, a Placeholder component that represents the List Item in both its visual and non-visual state is a good way to manage the items load on performance and memory. Assume each item has a payload of:

{ image: "<image_url>", background: "<background_color>" }

A Placeholder component can be constructed such that we always use the background color regardless if the item is visible, and then only show the image if it is visible using the Native Module calls we defined above. We can effectively build out the image when it’s not visible, and then manipulate the node to display or reset when appropriate.

A slight delay has been added to the image load to simulate poor network conditions where a solution like this can shine.

Image nodes being set at the scene tree level without creation or destruction of components

All list items are created, but only images are loaded when they are visible. In this extreme example, images are also reset (and unloaded from the GPU) when they go out of visible range minimizing memory used by the application while still providing a great user experience.

note: For the best experience while minimizing memory footprint, you will want to adjust the visible items so that items do load off screen, perhaps two screens worth, in order to make the transitions seamless for the user

Using the After Effects Workflow

While the above was all done using pure JSX and core Facebook React Native Components, everything works in an identical fashion when using You.i Engine React Native Components (such as ListRef , ViewRef , ImageRef ) and referencing components from the After Effects Workflow. While the Components may differ, the underlying Scene Tree node will be the same.

Class that represents interacting with AE Workflow Component

As an added benefit when using the Workflow, calling reset not only unloads the image from the GPU but also resets the state of the CYIImageView and all associated helper timelines.

After Effects Composition to represent List Item

This means if you employ an ImageSet timeline on your node, the next time the uri is set on that image the timeline will once again play. This means you can once again put the power of design and managing state into the hands of the designer.