Welcome to the last post in our series of articles that explain the techniques used in the UI Toolkit sample, QuizU, now updated to Unity 6.1.
Make sure you download the project from the Unity Asset Store and that you’re running on Unity 6.1 to follow along.
Thanks to all of you that have posted your questions and comments to this series.
As we’ve said before, QuizU is meant to be an instructional sample that teaches techniques for implementing programming design patterns into your game UI.
The other posts in this series are:
Post 1: The QuizU UI Toolkit sample: Now updated for Unity 6
Post 2: QuizU: Managing menu screens in UI Toolkit
Post 3: QuizU: State patterns for game flow
Post 4: QuizU: The Model View Presenter pattern
Post 5: QuizU: Event handling in UI Toolkit
Post 6: QuizU: Vector API
Also, in case you missed it, QuizU is not our only UI Toolkit demo. It complements two big pieces of content to help you get started with UI Toolkit:
-
UI Toolkit sample – Dragon Crashers: This demo is on the Asset. It’s a slice of a full-featured interface added to the 2D project Dragon Crashers, a mini RPG, using the Unity 6 UI Toolkit workflow at runtime.
-
UI Toolkit for advanced Unity developers (Unity 6 edition): This free e-book covers UI design and art creation fundamentals, and then moves on to instructional sections on UI development in Unity, mainly with UI Toolkit, but also with a chapter covering Unity UI.
Today’s post provides you with some general optimization tips when using UI Toolkit.
Building complex UIs with UI Toolkit can enhance performance, but you’ll still want to be mindful of best practices when deploying it.
Consider these tips and techniques for getting the most out of UI Toolkit.
Pre-create hierarchies rather than instantiating at runtime.
Hierarchy and elements
-
Pre-create hierarchies: Instantiating templates at runtime can slow your application. Pre-create your hierarchies in the Editor or during loading or non-peak periods to optimize performance such as when you are showing a menu or similar.
-
Keep the visible element count low: More visible elements require more processing. Try to keep your UI lean and only display what is necessary.
-
Use DisplayStyle.None for hidden/offscreen elements: Instantiating a complex hierarchy of elements is a relatively slow operation. Pre-creating UI elements and setting them to DisplayStyle.None can improve performance, but balance this with memory usage.
-
Pool recurring elements: For elements that frequently appear and disappear, utilizing an object pooling system can be beneficial. Rather than constantly creating and destroying these elements, you maintain a set of them in a pool. When needed, you simply reuse and recycle.
-
Use ListView for lists: ListView provides built-in pooling functionality, which can significantly improve performance for list-type UIs. ListView only renders the visible elements and recycles them as you scroll through the list, making it an efficient choice for long lists.
ListView provides built-in pooling for performance.
Implement custom culling: For non-ListView elements, you can implement custom culling using GeometryChangedEvent and VisualElement.layout property to manage the visibility and size of child elements.
Listen for the GeometryChangedEvent to know when a visual element’s layout (size/position) property changes. If so, then check the VisualElement.layout against the container displaying it. If the element is outside of this visible area, you can cull the child element by making it invisible.
Leverage UsageHints: Use the usageHints property to inform the system about how an element will be used at runtime. UsageHints can help optimize data storage and performance.
For instance, if you know that a particular element is going to be frequently updated (e.g., a score counter), you might set its UsageHints to DynamicTransform or DynamicMaterial, which tells the system to optimize the element for frequent updates.
Set the usageHints property before the VisualElement is part of a Panel. Otherwise, it is read-only.
Asset loading
Optimize asset loading: Load assets or scenes during opportune moments, such as when loading screens or transitions, to minimize disruptions to gameplay.
Understand UXML/USS dependencies: UXML/USS assets preload their dependencies, such as images referenced from the UI Builder. This is generally a good thing, but it can lead to inefficiencies if not managed correctly. Be aware of these dependencies to avoid unnecessary redundancy.
Mind your textures: Excessive textures can lead to an increase in draw calls. Be smart about texture usage and batching to maintain smooth rendering performance.
Utilize dynamic atlas systems: Small UI textures like icons can be added to an in-memory texture atlas. Misconfigured textures, however, can lead to unnecessary batch breaks. Doublecheck texture import settings, texture formats, mipmaps, non-power of two sizes, and compression settings when setting up a dynamic atlas.
Group textures with Sprite Atlases: You can use Sprite Atlases to group together textures used at the same time, which might be preferable to an auto-managed atlas.
Check texture memory in the Profiler.
UQuery
Cache Query results: UQuery requires traversing the hierarchy, which can be computationally expensive. Cache your query results for reuse to reduce overhead.
Leverage UQueryBuilder and the UQueryState struct: Avoid creating lists and optimize your queries by using UQueryBuilder and UQueryState. UQueryBuilder is a class used to construct queries to select specific UI elements based on various constraints like name, type, or class. UQueryState represents the result of an executed query. It contains the matching UI elements and allows iteration and manipulation of these elements.
For large UIs, querying for elements using UQuery can be more efficient than iterating over a list, especially when the query results in a small subset of elements. Unlike a list, the UQuery system does not require a full iteration.
Garbage collection
Be mindful of garbage collection: Unity’s garbage collection can feel like a black box, but try not to retain references to visual elements in classes that outlive the UIDocument or Window where they originated.
Be cautious about closures and lambda functions, which might accidentally retain a reference to a visual element, that won’t be garbage collected as long as the closure exists. This can accidentally lead to memory leaks and performance issues if not managed carefully.
Incremental GC can be beneficial: With high memory pressure, garbage collection (GC) spikes can occur. In the Project Settings window, find the Configuration section, and check the box next to Enable Incremental GC to help alleviate this.
Enabling Incremental Garbage Collection can help reduce memory pressure.
Rendering
Understand UI Toolkit’s shader approach: UI Toolkit uses a single shader approach, so each draw call isn’t as costly as a full material change. Understand the trade-offs to optimize your UI (e.g. you might not be able to incorporate certain visual or shader effects into your UI if they require a different shader).
Avoid expensive tessellation: Any time the size (width/height) of an element changes, its geometry re-builds. This can be computationally expensive.
Instead of frequently changing the size of UI elements, you can use transforms for animations or adjustments. Transforms are less performance-intensive as they don’t trigger tessellation.
Similarly, using textures or 9-sliced sprites can be a better choice for rounded corners or borders. These alternatives do not trigger tessellation and can be more efficiently reused across different UI elements.
Choose the right font population mode: Use Static Font Assets for known text, such as labels and titles. They are fast and efficient, as they pre-bake characters into the atlas texture and don’t require the source font file at build time. Dynamic OS Font Assets reduce memory overhead since the font source files don’t need to be included in the build. Dynamic Font Assets incur additional performance overhead because the source font files must be included in the build.
Choose the right atlas mode for fonts: In terms of rendering, use Bitmap for pixel-perfect alignment, ideal for pixel art. For fonts that might undergo transformations or need special effects like outlines, opt for Signed Distance Field (SDF) rendering for a crisper and more adaptable presentation.
Styles and selectors
Simplify USS selectors: Complex hierarchical selectors can negatively impact performance. Use the Block-Element-Modifier convention to reduce complexity, and avoid the “*” selector as it requires every potential element to be tested against the selector.
USS selector cheat sheet (avoid the * selector).
Use :hover sparingly: While :hover can enhance interactivity, it can trigger a redrawing of the hierarchy of targeted elements and their children. This could lead to significant performance overhead, especially if the hierarchy is complex or the changes are frequent.
Avoid inline styles: Inline styles have per-element memory overhead and require more processing to resolve the final style of an element. Favor using USS rules for efficient memory usage and better performance.
Balancing Performance and Usability
Of course, remember that optimization is both an art and a science. Some of these suggestions may lead to increased code complexity or a change of workflow, so factor that in before attempting a specific technique.
For more general tips about performance optimization in UI Toolkit, be sure to see the manual page in the documentation.
Be aware of complex hierarchies when using the :hover pseudo-state
Remember to profile your UI to identify potential performance bottlenecks and optimize based on your specific project needs. Optimization often involves a trade-off between performance and usability. Always strive to strike the right balance for your specific use case.
In most cases, you will need to profile to see how these techniques can tangibly improve your specific project. Then, decide as a team which ones to implement.
Further reading
We hope that the QuizU project can help you get started with UI Toolkit to create UI systems for your Unity projects. Remember that you can always find more support in the UI Discussions forum and UI Toolkit documentation.
If you’re interested in more software design patterns, please make sure to see the free e-book Level up your code with game programming patterns and SOLID. More Unity best practices guides are available in documentation.






