ドキュメント

Unity Engine


User Manual

Script Reference

Unity Engine


Best practices for managing elements

Things you can do to improve performance when you manage elements in a UI.
読み終わるまでの所要時間 6 分最終更新 1ヶ月前

This page describes the best practices for managing elements in the visual tree.

Pool recurring elements

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.
When you implement a custom object pool for VisualElements, take full control of all pooled elements and reset them before returning them to the pool. UI Toolkit provides internal pooling for systems like events (EventBase) to improve performance, but it doesn't include a built-in pool for VisualElements. If you create your own pool, you must handle cleanup. For example, if you register an event callback on an element and return it to your pool without un-registering it, the callback remains. This can cause instability, unexpected behavior, and memory leaks when the element is reused.

Keep the number of visible elements low

To keep the number of visual elements 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:

Different approaches to hiding an element

When 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. Refer to the following table for a summary of the different approaches:

Summary of hiding elements

Hide with USS style
visibility: hidden;

With this approach, the descendants can override the
visibility
style.

Single-frame cost

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;

StylesEvaluated for the element and descendants to propagate the visibility.Evaluated for it and the descendants, to propagate the visibility.
Layout dataPreservedNone
Rendering commandsRemoved and deallocatedRecreated and reinserted into the chain of commands.
MeshesScheduled for deallocation.Re-tessellated

Per-frame behavior

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

CPUStylesFully evaluated to the element and its descendants.
Layout dataUpdated
TessellationMinimal impact that only involves stencil masking meshes, if applicable.
Rendering commandsNo 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.
GPUMeshesVertex and fragment shading on stencil masking meshes.

Hide with USS style
opacity: 0;

With this approach, some GPU impact can be expected, as the vertex shader processes all elements.

Single-frame cost

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 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

Per-frame behavior

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

CPUStylesFully evaluated to the element and its descendants.
TessellationOperates normally and responds to changes.
Rendering commandsExecuted
GPUMeshesThe vertex shader operates as though the
visibility
is set to
1
, which can be detrimental in GPU-bound projects if there are many vertices to process. Triangles are moved offscreen so there is no fragment shader cost.

Hide with USS style
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.

Single-frame cost

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 dataMight recompute the layout of other elements.Pending layout changes are processed.
Rendering commands/MeshesRegenerated for elements affected by the layout change.
  • Regenerated for elements affected by the layout change.
  • Pending changes are processed.

Per-frame behavior

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 dataPreserved but can become invalidated and not being updated.
Rendering commandsAlthough 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.
MeshesRetained but can become invalidated and not being updated.

Translate outside of the Viewport

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.

Single-frame cost

The transform is computed and uploaded into GPU memory, which is generally fast.

Per-frame behavior

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

CPUStylesUpdated
Layout dateUpdated
Draw callsExecuted
GPUMeshesVertices are shaded.

Remove from the hierarchy

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.

Single-frame cost

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

StylesNoneUpdated for the subtree.
LayoutNoneRecomputed for the subtree and possibly other elements.
Rendering commands/meshesRegenerated for elements affected by the layout change.
  • Regenerated for elements affected by the layout change.
  • Pending changes are processed.

Memory usage after being hidden

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

CPUStylesRetainedRetainedRetainedRetainedFreed
LayoutRetainedRetainedRetainedRetainedRetained[1]
Rendering commands/meshesFreedRetainedRetainedRetainedFreed
GPUMeshesFreedRetainedRetainedRetainedFreed
[1]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.