LumaPictures / usd-qt

Reusable Qt Components for Pixar's USD
Other
153 stars 41 forks source link

Invalidate hierarchy cache? #26

Open csaez opened 5 years ago

csaez commented 5 years ago

Hi there,

I'm trying to debug/isolate an issue on a stage modified procedurally by an internal tool (think of a graph with nodes applying edits on the stage, the user can step through nodes seeing the chain of changes applied till that point).

Unfortunatelly I don't have a simple repro, but I'm seeing something like this before/after the modification and I suspect the cache might be associating the wrong QModelIndex to the new prims.

                                          +
                                          |
   BEFORE                   AFTER         |   DESIRED RESULT
                                          |
  /<root>                /<root>          |     /<root>
    PARENT1                NEW_PRIM       |       NEW_PRIM
      CHILD1   +----->       CHILD1       |       PARENT1
    PARENT2                PARENT1        |         CHILD1
      CHILD2                 CHILD2       |       PARENT2
                           PARENT2        |         CHILD2
                                          |
                                          +

Is there a relatively simple way to invalidate the hierarchy cache on the model?

I tried by overriding the model._index with a fresh instance (similar to ResetStage), but the view loses its state (i.e. selection, expanded items, scroll) and was wondering if there's already a more straight forward way to do this.

https://github.com/LumaPictures/usd-qt/blob/eae0a959c6e8d7f9be8a8a69dc543eff7ec96075/pxr/usdQt/hierarchyModel.py#L113-L114

Thanks!

nrusch commented 5 years ago

Hi @csaez,

It's a little hard to guess at whether this may be an issue with the HierarchyCache (which doesn't deal with model indices at all), or with the stage notice handler on the HierarchyModel that's responsible for translating stage changes into updates to its persistent model indices.

The HierarchyCache does expose a ResyncSubtrees method, which takes a list of "parent" prim paths and rebuilds their child hierarchies, but this is not likely to be particularly useful by itself; you could call the method on the model's associated cache, but there would be nothing informing the model that it needed to rebuild any of its persistent indices, so you would likely get errors or just a crash afterward.

The brute-force method of getting a fresh hierarchy cache would be to call:

model.ResetStage(None)
model.ResetStage(theRealStage)

While that obviously isn't the best long-term solution, it may at least help you determine the source of the inconsistency you're seeing.

You may be able to get some more information about what the cache is doing by enabling the Tf debug code USDQT_DEBUG_HIERARCHYCACHE.

csaez commented 5 years ago

Thanks @nrusch,

I can confirm that our issues are gone by invalidating the cache. As a short-term measure we ended up adding a method to explicitly invalidate the cache by doing the following (pseudo code):

def invalidateCache(self):
    model = self.model()
    if not model:
        return False

    expandedState = [index.data(QtCore.Qt.DisplayRole)
                     for index in model.persistentIndexList()
                     if self.isExpanded(index)]

    model.beginResetModel()
    model._index = _HierarchyCache(self.stage.GetPrimAtPath('/'), model._predicate)
    model.endResetModel(model.index(0, 0))

    self._restoreExpandedState(expandedState, model.index(0, 0))
    return True

def _restoreExpandedState(self, state, index)
    for each in expandedState:
        for index in model.match(index, QtCore.Qt.DisplayRole, each):
            if not index:
                continue
            control.setExpanded(model.index(0, 0, index), True)
            self._restoreExpandedState(expandedState, index)

Does this looks reasonable to you? Would you be ok accepting a patch like this? Otherwise we are totally cool keeping it on our side, just checking :+1:

Thanks!