Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HYDRA-747 : Filtering scene index example can have prims get unfiltered at the wrong location #42

Merged
merged 9 commits into from
Feb 2, 2024
120 changes: 111 additions & 9 deletions lib/flowViewport/API/samples/fvpFilteringSceneIndexExample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
#include <pxr/imaging/hd/meshSchema.h>
#include <pxr/imaging/hd/tokens.h>

#include <stack>

PXR_NAMESPACE_OPEN_SCOPE

namespace
{
//As an example, we use a filtering scene index to filter mesh primitives which have more than 10 000 vertices.
bool IsFiltered(const HdSceneIndexPrim& sceneIndexPrim)
bool ShouldBeFiltered(const HdSceneIndexPrim& sceneIndexPrim)
{
static bool _hideCameras = false;
static bool _hideSimpleLights = false;
Expand Down Expand Up @@ -64,20 +66,120 @@ namespace
}

namespace FVP_NS_DEF {
bool FilteringSceneIndexExample::IsFiltered(const SdfPath& primPath) const
{
return _filteredPrims.find(primPath) != _filteredPrims.end();
}

void FilteringSceneIndexExample::UpdateFilteringStatus(const SdfPath& primPath)
{
if (ShouldBeFiltered(_GetInputSceneIndex()->GetPrim(primPath))) {
_filteredPrims.insert(primPath);
} else {
_filteredPrims.erase(primPath);
}
}

FilteringSceneIndexExample::FilteringSceneIndexExample(const HdSceneIndexBaseRefPtr& inputSceneIndex)
: ParentClass(inputSceneIndex)
{
std::stack<SdfPath> primPathsToTraverse({ SdfPath::AbsoluteRootPath() });
while (!primPathsToTraverse.empty()) {
SdfPath currPrimPath = primPathsToTraverse.top();
primPathsToTraverse.pop();
UpdateFilteringStatus(currPrimPath);
for (const auto& childPath : inputSceneIndex->GetChildPrimPaths(currPrimPath)) {
primPathsToTraverse.push(childPath);
}
}
}

//This is the function where we filter prims
HdSceneIndexPrim FilteringSceneIndexExample::GetPrim(const SdfPath& primPath) const
{
if (_GetInputSceneIndex()){
const HdSceneIndexPrim prim = _GetInputSceneIndex()->GetPrim(primPath);

const bool isThisPrimFiltered = IsFiltered(prim);
if ( ! isThisPrimFiltered){
return prim;//return only non filtered prims
return IsFiltered(primPath) ? HdSceneIndexPrim() : _GetInputSceneIndex()->GetPrim(primPath);
}

SdfPathVector FilteringSceneIndexExample::GetChildPrimPaths(const SdfPath& primPath) const {
// A filtered prim should not exist from the point of view of downstream scene indices,
// so return an empty vector if the current prim is filtered. This case should normally
// not be reached during scene index hierarchy traversal, as its parent should not even
// return it when GetChildPrimPaths is called on it (see other comment in this method.)
if (IsFiltered(primPath)) {
return SdfPathVector();
}

// If the current prim is not filtered, we still do not want to return a path
// to a filtered child prim, as a filtered prim should not exist at all (and
// we might have sent a PrimsRemoved notification prior). Thus, remove all
// child paths to filtered prims before returning.
SdfPathVector childPaths = _GetInputSceneIndex()->GetChildPrimPaths(primPath);
lanierd-adsk marked this conversation as resolved.
Show resolved Hide resolved
childPaths.erase(
std::remove_if(
childPaths.begin(),
childPaths.end(),
[this](const SdfPath& childPath) -> bool { return IsFiltered(childPath); }),
childPaths.end());
return childPaths;
}

void FilteringSceneIndexExample::_PrimsAdded(
const HdSceneIndexBase& sender,
const HdSceneIndexObserver::AddedPrimEntries& entries)
{
HdSceneIndexObserver::AddedPrimEntries unfilteredEntries;
for (const auto& entry : entries) {
// We only want to forward the notifications for prims that don't get filtered out
UpdateFilteringStatus(entry.primPath);
if (!IsFiltered(entry.primPath)) {
unfilteredEntries.push_back(entry);
}
}
_SendPrimsAdded(unfilteredEntries);
}

return HdSceneIndexPrim();
void FilteringSceneIndexExample::_PrimsRemoved(
const HdSceneIndexBase& sender,
const HdSceneIndexObserver::RemovedPrimEntries& entries)
{
for (const auto& entry : entries) {
// We don't need to update or check the filtering status, since the prim is getting removed either way
_filteredPrims.erase(entry.primPath);
}
_SendPrimsRemoved(entries);
}

void FilteringSceneIndexExample::_PrimsDirtied(
const HdSceneIndexBase& sender,
const HdSceneIndexObserver::DirtiedPrimEntries& entries)
{
// There are three potential scenarios here for a given prim :
// 1. Its filtering status did NOT change -> forward the PrimsDirtied notification as-is
// 2. Its filtering status DID change :
// 2a. If the prim was previously filtered -> it is now unfiltered, so send a PrimsAdded notification
// 2b. If the prim was previously unfiltered -> it is now filtered, so send a PrimsRemoved notification
HdSceneIndexObserver::AddedPrimEntries newlyUnfilteredEntries;
HdSceneIndexObserver::RemovedPrimEntries newlyFilteredEntries;
HdSceneIndexObserver::DirtiedPrimEntries dirtiedEntries;
for (const auto& entry : entries) {
bool wasPreviouslyFiltered = IsFiltered(entry.primPath);
UpdateFilteringStatus(entry.primPath);
if (wasPreviouslyFiltered == IsFiltered(entry.primPath)) {
// Filtering status did not change, forward notification as-is
dirtiedEntries.push_back(entry);
}
else {
// Filtering status changed, send a different notification instead
if (wasPreviouslyFiltered) {
newlyUnfilteredEntries.emplace_back(
entry.primPath, _GetInputSceneIndex()->GetPrim(entry.primPath).primType);
} else {
newlyFilteredEntries.emplace_back(entry.primPath);
}
}
}
_SendPrimsAdded(newlyUnfilteredEntries);
_SendPrimsRemoved(newlyFilteredEntries);
_SendPrimsDirtied(dirtiedEntries);
}

}//end of namespace FVP_NS_DEF
Expand Down
33 changes: 12 additions & 21 deletions lib/flowViewport/API/samples/fvpFilteringSceneIndexExample.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,41 +46,32 @@ class FilteringSceneIndexExample : public HdSingleInputFilteringSceneIndexBase
}

// From HdSceneIndexBase
HdSceneIndexPrim GetPrim(const SdfPath& primPath) const override;//Is the useful function where we do filtering
HdSceneIndexPrim GetPrim(const SdfPath& primPath) const override;

SdfPathVector GetChildPrimPaths(const SdfPath& primPath) const override{//We leave this function with no filtering for simplicity
if (_GetInputSceneIndex()){
return _GetInputSceneIndex()->GetChildPrimPaths(primPath);
}

return {};
}
SdfPathVector GetChildPrimPaths(const SdfPath& primPath) const override;

~FilteringSceneIndexExample() override = default;

protected:
FilteringSceneIndexExample(const HdSceneIndexBaseRefPtr& inputSceneIndex) : ParentClass(inputSceneIndex) {}
FilteringSceneIndexExample(const HdSceneIndexBaseRefPtr& inputSceneIndex);

void _PrimsAdded(
const HdSceneIndexBase& sender,
const HdSceneIndexObserver::AddedPrimEntries& entries) override final
{
_SendPrimsAdded(entries);
}
const HdSceneIndexObserver::AddedPrimEntries& entries) override;

void _PrimsRemoved(
const HdSceneIndexBase& sender,
const HdSceneIndexObserver::RemovedPrimEntries& entries) override
{
_SendPrimsRemoved(entries);
}
const HdSceneIndexObserver::RemovedPrimEntries& entries) override;

void _PrimsDirtied(
const HdSceneIndexBase& sender,
const HdSceneIndexObserver::DirtiedPrimEntries& entries) override
{
_SendPrimsDirtied(entries);
}
const HdSceneIndexObserver::DirtiedPrimEntries& entries) override;

bool IsFiltered(const SdfPath& primPath) const;

void UpdateFilteringStatus(const SdfPath& primPath);

SdfPathSet _filteredPrims;
};

}//end of namespace FVP_NS_DEF
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions test/lib/mayaUsd/render/mayaToHydra/testFlowViewportAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ def test_FilteringPrimitives(self):
self.setHdStormRenderer()
self.assertSnapshotClose("filter_VP2AndThenBackToStorm.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT)

#Test unfiltering the sphere after switching renderers (HYDRA-747)
self.setViewport2Renderer()
cmds.xform(sphereNode, translation=[0,5,0], scale=[4,4,4])
self.setHdStormRenderer()
cmds.setAttr(sphereShape + '.subdivisionsAxis', 30) #Unfilter the prim
cmds.refresh()
self.assertSnapshotClose("filter_VP2AndThenBackToStorm_MovedSphereUnFiltered.png", self.IMAGE_DIFF_FAIL_THRESHOLD, self.IMAGE_DIFF_FAIL_PERCENT)

#Finish by a File New command
cmds.file(new=True, force=True)

Expand Down
Loading