This page describes the best practices for managing elements in the visual treeAn object graph, made of lightweight nodes, that holds all the elements in a window or panel. It defines every UI you build with the UI Toolkit.
See in Glossary.
Elements pooling is to keep hold of elements that you might recreate later on, rather than creating elements with new()
every time and letting go of them.
It’s important to be fully in control of all elements that you pool and make sure you reset them properly before you return them to the pool. Otherwise, the pooling system can become unstable and troublesome. For example, it’s impossible to clean up an element if you pool elements while registering event callbacks or setting an internal non-serialized state at the same time.
To keep the number of visual elementsA node of a visual tree that instantiates or derives from the C# VisualElement
class. You can style the look, define the behaviour, and display it on screen as part of the UI. More info
See in Glossary low, use ListView when possible. ListView pools elements and recycles elements as the user scrolls.
Alternatively, you can implement your own pool and recycle mechanism similar to the ListView, and use the following to manage the visible area:
GeometryChangedEvent
VisualElement.layout
propertyWhen you use VisualElement.RemoveFromHierarchy()
to remove an element from the hierarchy and eliminate references to it, the element is garbage collected. This reduces the CPU and GPU cost to zero and frees a significant amount of memory. However, it’s a slow and costly operation to recreate elements and reload them in a hierarchy. To avoid this, you can pre-create the elements in a hierarchy, use USS style properties to hide them, and only display them when necessary. While applying styles is generally faster, it can lead to increased memory usage if you create a large number of elements simultaneously.
The following describes the different approaches to hiding elements and the consequences on processors and memory usage.
visibility: hidden;
With this approach, the descendants can override the visibility
style.
The following table describes the different aspects of single-frame cost when you hide or display a visual element with the visibility
style:
Aspect | visibility: hidden; |
visibility: visible; |
---|---|---|
Styles | Evaluated for the element and descendants to propagate the visibility. | Evaluated for it and the descendants, to propagate the visibility. |
Layout data | Preserved | None |
Rendering commands | Removed and deallocated | Recreated and reinserted into the chain of commands. |
Meshes | Scheduled for deallocation. | Re-tessellated |
The following table describes the per-frame behavior for CPU and GPU when you hide a visual element with the visibility
style:
Processor | Aspect | Per-frame behavior |
---|---|---|
CPU | Styles | Fully evaluated to the element and its descendants. |
Layout data | Updated | |
Tessellation | Minimal impact that only involves stencil masking meshesOverflow hidden with either rounded corners or vector image background. See in Glossary, if applicable. |
|
Rendering commands | No commands to draw regular visible geometry. However, stencil masking meshes are still rendered to push or pop from the stencil, ensuring potential visible descendants are masked. | |
GPU | Meshes | Vertex and fragment shading on stencil masking meshes. |
opacity: 0;
With this approach, the GPU usage can be high if the content is in the ViewportThe user’s visible area of an app on their screen.
See in Glossary, as the fragment shaderA program that runs on the GPU. More info
See in Glossary processes all elements, potentially leading to significant overdraw.
The following table describes the single-frame cost when you hide or display a visual element with the opacity
style:
Action | Single-frame cost |
---|---|
opacity: 0; |
The first time when you set the opacity to a value other than 1 , the UI(User Interface) Allows a user to interact with your application. Unity currently supports three UI systems. More infoSee in Glossary Toolkit renderer modifies the vertices to accelerate the application of the opacity on GPU. This triggers a one-time, minimal CPU cost. While typically negligible, this cost might become noticeable if the element has a large number of descendants or if there are many vertices to modify. This cost is not incurred again unless the element is removed from the visual tree and re-added. |
opacity: 1; |
None |
The following table describes the per-frame behavior for CPU and GPU when you hide a visual element with the opacity
style:
Processor | Aspect | Per-frame behavior |
---|---|---|
CPU | Styles | Fully evaluated to the element and its descendants. |
Tessellation | Operates normally and responds to changes. | |
Rendering commands | Executed | |
GPU | Meshes | The vertex shaderA program that runs on each vertex of a 3D model when the model is being rendered. More info See in Glossary operates as though the visibility is set to 1 . Similarly, the fragment shader also functions as if the visibility is 1 . This can be detrimental in GPU-bound projects as it can lead to overdraw. |
display: none;
With this approach, the element behaves like it is removed from the layout tree, which might potentially affect the layout of other elements.
The following table describes the different aspects of single-frame cost when you hide or display a visual element with the display
style:
Aspect | display: none; |
display: flex; |
---|---|---|
Layout data | Might recompute the layout of other elements. | Pending layout changes are processed. |
Rendering commands/Meshes | Regenerated for elements affected by the layout change. |
|
The following table describes the per-frame behavior of the CPU when you hide a visual element with the display
style. Note that there’s no GPU cost.
Aspect | Per-frame behavior |
---|---|
Layout data | Preserved but can become invalidated and not being updated. |
Rendering commands | Although retained, they can be skipped during execution. The way they are skipped is very cheap but not entirely free. The cost is proportional to the number of commands. |
Meshes | Retained but can become invalidated and not being updated. |
You can use translate: -5000px -5000px;
combined with DynamicTransform
usage hints to move the elements out of the Viewport. The geometry remains fully active, resulting in minimal CPU usage when you bring back the element on the screen. However, the GPU continues to process the vertices, which might be acceptable depending on the scenario.
The transform is computed and uploaded into GPU memory, which is generally fast.
The following table describes the per-frame behavior for CPU and GPU when you hide a visual element by translating it outside of the Viewport:
Processor | Aspect | Per-frame behavior |
---|---|---|
CPU | Styles | Updated |
Layout date | Updated | |
Draw calls | Executed | |
GPU | Meshes | Vertices are shaded. |
When you use the VisualElement.RemoveFromHierarchy()
method to remove the element from the hierarchy, you free up CPU and GPU memories, thereby eliminating any computing costs.
The following table describes the single-frame cost when you hide or display a visual element by removing it from the hierarchy:
Aspect | Remove | Add |
---|---|---|
Styles | None | Updated for the subtree. |
Layout | None | Recomputed for the subtree and possibly other elements. |
Rendering commands/meshes | Regenerated for elements affected by the layout change. |
|
The following table summarizes the memory usage after the element is hidden with different approaches:
Processor | Aspect | visibility:hidden; |
opacity:0; |
display:None; |
Translated out of Viewport | Removed from the hierarchy |
---|---|---|---|---|---|---|
CPU | Styles | Retained | Retained | Retained | Retained | Freed |
Layout | Retained | Retained | Retained | Retained | Retained[1] | |
Rendering commands/meshes | Freed | Retained | Retained | Retained | Freed | |
GPU | Meshes | Freed | Retained | Retained | Retained | Freed |
The layout memory is retained because it remains reserved for the element. When the VisualElement is garbage collected, the layout memory is returned to the pool, making it available for use by other elements. ↩
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.
When you visit any website, it may store or retrieve information on your browser, mostly in the form of cookies. This information might be about you, your preferences or your device and is mostly used to make the site work as you expect it to. The information does not usually directly identify you, but it can give you a more personalized web experience. Because we respect your right to privacy, you can choose not to allow some types of cookies. Click on the different category headings to find out more and change our default settings. However, blocking some types of cookies may impact your experience of the site and the services we are able to offer.
More information
These cookies enable the website to provide enhanced functionality and personalisation. They may be set by us or by third party providers whose services we have added to our pages. If you do not allow these cookies then some or all of these services may not function properly.
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us to know which pages are the most and least popular and see how visitors move around the site. All information these cookies collect is aggregated and therefore anonymous. If you do not allow these cookies we will not know when you have visited our site, and will not be able to monitor its performance.
These cookies may be set through our site by our advertising partners. They may be used by those companies to build a profile of your interests and show you relevant adverts on other sites. They do not store directly personal information, but are based on uniquely identifying your browser and internet device. If you do not allow these cookies, you will experience less targeted advertising. Some 3rd party video providers do not allow video views without targeting cookies. If you are experiencing difficulty viewing a video, you will need to set your cookie preferences for targeting to yes if you wish to view videos from these providers. Unity does not control this.
These cookies are necessary for the website to function and cannot be switched off in our systems. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms. You can set your browser to block or alert you about these cookies, but some parts of the site will not then work. These cookies do not store any personally identifiable information.