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.
- Missing removal notices. Erasing an entry from the internal map does not tell observers that the prim disappeared. The code must send
PrimsRemovedwith aRemovedPrimEntryfor/World/Selection(and for any selected-child paths) after the retained data is removed. - Over-broad dirtying.
_SendPrimsDirtiedon/Worldwith an emptyHdDataSourceLocatorSetinvalidates 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 thexformlocator for a transform change or a visibility locator for show/hide. - Ordering.
GetPrimand 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 intoGetPrimand 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/0with a locator set containing thexformlocator, not/World. - When selection changes, send
PrimsRemovedfor the old highlight andPrimsAddedfor 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.