NCP-OUSD study materials
NCP-OUSD study resources

Selection highlight that flickers and sticks

NCP-OUSD · Put your knowledge to work in context.

All case studies

Selection highlight that flickers and sticks

A studio adds a custom HdSceneIndexBase subclass, SelectionHighlightSceneIndex, above the stage scene index. On selection, it mutates an internal map of prims and calls _SendPrimsAdded for a cube at /World/Selection. On deselection, it erases the map entry but sends no notice. It also calls _SendPrimsDirtied on /World with an empty locator set every frame to force a refresh. In usdview with Storm, the cube flickers, the viewport redraws completely each frame, and after deselection the cube remains until a frame rebuilds the scene index. GetPrim sometimes returns the previous selection because the map is updated after the notice is sent. They need multiple selections and a transform update when the selected object moves, without rebuilding.

1. Identify the notice-protocol violations and describe the correct add/remove/dirty notifications for selection changes.

Read the worked answer

The scene index is violating the observer contract in three ways.

  1. Missing removal notices. Erasing an entry from the internal map does not tell observers that the prim disappeared. The code must send PrimsRemoved with a RemovedPrimEntry for /World/Selection (and for any selected-child paths) after the retained data is removed.
  2. Over-broad dirtying. _SendPrimsDirtied on /World with an empty HdDataSourceLocatorSet invalidates the entire subtree. That is effectively a full-scene invalidation, not a targeted refresh. Dirty only the changed prim and only the changed locator, such as the xform locator for a transform change or a visibility locator for show/hide.
  3. Ordering. GetPrim and observers must see a consistent state. Update the internal retained data first, then send the notice. If the notice is sent before the map is updated, a synchronous observer can call back into GetPrim and receive the old prim, which explains the stale selection reads.

For multiple selections, keep stable paths such as /World/Selection/0, /World/Selection/1, and send PrimsAdded only for newly created highlight prims, PrimsRemoved only for removed ones, and PrimsDirtied only for highlights whose transform or visibility changed. Do not re-add an unchanged prim every frame.

2. Why does the whole viewport redraw each frame, and how should the implementation dirty only what changed?

Read the worked answer

The whole viewport redraws because the scene index sends a dirty notice on /World with an empty locator set every frame. In Hydra, an empty locator set on a prim means "all data sources on this prim and its descendants may have changed." The render index therefore marks the entire /World subtree dirty and the render delegate cannot reuse cached buffers, draw batches, or transforms.

The fix is to make dirtying proportional to the actual change:

  • When the highlight cube moves, dirty only /World/Selection/0 with a locator set containing the xform locator, not /World.
  • When selection changes, send PrimsRemoved for the old highlight and PrimsAdded for the new one, rather than dirtying an ancestor.
  • If only visibility changes, dirty the visibility data source, not the transform.
  • If the highlight is at a stable path and only its transform changes, keep the prim and update its retained transform data source, then send one targeted dirty notice.

Avoid using a per-frame "force refresh" dirty as a synchronization mechanism. It defeats Hydra's caching and can cause flicker when observers process the add and dirty notices in the same batch. Use HdNoticeBatchingSceneIndex if notices need to be coalesced, and let the render index rebuild only the affected prim.

3. The highlight must follow the selected object's transform for multiple selections. Recommend an approach that avoids path conflicts and unnecessary rebuilds.

Read the worked answer

Keep the highlight prims at a stable namespace, such as /World/Selection/0, rather than inserting a child under the selected prim. Reparenting into the selected prim would require changing the scene index hierarchy, can collide with existing child names, and forces observers to remove and re-add subtrees. It also makes it hard to support multiple selections that share an ancestor.

Instead, each frame or on selection change, read the selected prim's world transform from the stage or from the upstream scene index's transform data source, compose the highlight offset, and update the highlight prim's retained xform data source. Then send a targeted PrimsDirtied for that highlight path with the xform locator. For multiple selections, allocate one stable highlight path per selection slot and keep a mapping from selection ID to slot; reuse slots instead of creating unbounded paths.

If the highlight must be parented to the selected object for semantic reasons, treat it as a new prim insertion and send proper add/remove notices for the parent change. Do not mutate the hierarchy silently. For transform updates, prefer the stable-path approach because it keeps the scene index topology unchanged and confines invalidation to the highlight's transform.