Optimizing the Layout calls
In this post, we will cover how the Layout is done in Gameface and how it can be optimized since no user would want a laggy user interface caused by the constant movements of the UI elements.
Generally, the Layout calls of Gameface boil down to determining the sizes and positions of the elements in the DOM tree so that they can be laid out in the View texture. The layout and rendering of the page in Gameface happen by following these high-level steps:
- The HTML and CSS define the properties of the DOM elements
- A subset of these properties is fed to the layout solver to determine the location and size of the elements in the texture.
- Then the elements are rendered on the screen at their respective positions and size.
- When the next frame comes, checks are made to see if JavaScript and CSS animations change any element's properties.
- Go to step 2 and repeat.
In the above flow, the second step is the Layout one and can be further split into different phases of the process since there are two types of layout - full or partial. If the updated styles of the elements are only transformations, there is no need to invoke full layout solve - we will run a simpler layout pass limited to transformation updates.
- The full Layout of the DOM elements in Gamfeace is performed when there are changes to the layout properties during the DOM update for a given frame. In that case, we have to invoke the full Layout for the affected nodes and their subtrees to lay them out from scratch in the UI. For example, if the left/top CSS styles of a DOM element are changed, Gameface will request a full page layout to calculate the correct position of the HTML elements in the page's flow.
- The partial Layout will be performed only if the transform CSS style of the elements is changed for a given frame as there is considerably less work to be done. We do not need a full layout as the untransformed positions of the nodes have not been changed in any way. In this phase, we primarily want to figure out the AABB and visual AABB of each node, have an idea of what transformations we'll be rendering the nodes, how the transforms of the parent nodes propagate to child nodes, and how the AABBs of child nodes affect the AABBs of parent nodes. It is important to remember that the full layout phase will include this partial "layout", so we will update the transformations every time we have a full layout. This step is visible in the Google Chrome Dev Tools' Performance panel as "Update Transform Nodes":

With the above in mind, we must strive to fall into the partial layout case to build a performant UI where the elements would be moved constantly. A typical use case in which this technique can be applied is the moving nameplates in the games:

Using the same UI as in the GIF above and two different methods to move the nameplates - using “top”/”left” CSS styles and “transformX”/”transformY” we get twice as better performance in the latter case.
-
With “top”/”left” style we get an average of 0.043ms only for moving the nameplates in the Layout call:

-
With “transformX”/”transformY” we fully eliminate the need to invoke the Layout call and get an average of 0.023ms for the Update Node Transforms call, which would be the “partial” Layout:

In summary, if there is a use case that you can achieve by using only transformations, it is recommended compared to using positioning styles as the gains will be almost 50%.
-
I just wanted to say thanks, this sort of information is really useful to know and I appreciate these sorts of articles!
0 -
Glad to hear that! We plan to have more articles of this kind in the future :)
0
Please sign in to leave a comment.
Comments
2 comments