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

Ensure light collections are dirtied on creation #1653

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 45 additions & 4 deletions pxr/usdImaging/usdImaging/collectionCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,29 @@ _IsQueryTrivial(UsdCollectionAPI::MembershipQuery const& query)
ruleMap.begin()->second == UsdTokens->expandPrims;
}

void
UsdImaging_CollectionCache::_MarkCollectionContentDirty(UsdStageWeakPtr const& stage,
UsdCollectionAPI::MembershipQuery const& query)
{
if(query.HasExcludes())
{
// If there are any exlusion we have to consider all previously excluded paths dirty
// As there is no API to query excluded paths we mark all prims in the Stage dirty
_allPathsDirty = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if the query has exclude paths, I'd still expect UsdComputeIncludedPathsFromCollection to come up with the right list. That should return the set of paths that pass both the include & exclude rules, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, but prims that were previously excluded from the collection would not be returned in the include set, and these also need to be marked dirty...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually as the RemoveCollection call will catch all paths from the outgoing collection, the UpdateCollection should be able to safely ignore the excluded paths... Will test that further!

}
else
{
SdfPathSet linkedPaths = UsdComputeIncludedPathsFromCollection(query, stage);
std::merge(_dirtyPaths.begin(), _dirtyPaths.end(), linkedPaths.begin(), linkedPaths.end(),
std::inserter(_dirtyPaths, _dirtyPaths.begin()));
}
}

TfToken
UsdImaging_CollectionCache::UpdateCollection(UsdCollectionAPI const& c)
{
RemoveCollection(c);
const UsdStageWeakPtr& stage = c.GetPrim().GetStage();
RemoveCollection(stage, c.GetCollectionPath());

std::lock_guard<std::mutex> lock(_mutex);

Expand Down Expand Up @@ -78,15 +97,16 @@ UsdImaging_CollectionCache::UpdateCollection(UsdCollectionAPI const& c)
_pathsForQuery[query].insert(path);
_idForPath[path] = id;

_MarkCollectionContentDirty(stage, query);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like there's some missing bits here... If _queryForId[id] before you update it != query, also _MarkContentDirty(stage, _queryForId[id])?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RemoveCollection call at the beginning of UpdateCollection will mark the previous collection content dirty, wouldn't this cover it?

return id;
}

void
UsdImaging_CollectionCache::RemoveCollection(UsdCollectionAPI const& c)
UsdImaging_CollectionCache::RemoveCollection(UsdStageWeakPtr const& stage, SdfPath const& path)
{
std::lock_guard<std::mutex> lock(_mutex);

SdfPath path = c.GetCollectionPath();
auto const& pathEntry = _idForPath.find(path);
if (pathEntry == _idForPath.end()) {
// No pathEntry -- bail. This can happen if the collection was
Expand All @@ -102,6 +122,9 @@ UsdImaging_CollectionCache::RemoveCollection(UsdCollectionAPI const& c)
return;
}
UsdCollectionAPI::MembershipQuery const& queryRef = queryEntry->second;
_idForQuery.erase(queryRef);

_MarkCollectionContentDirty(stage, queryRef);

auto const& pathsForQueryEntry = _pathsForQuery.find(queryRef);
pathsForQueryEntry->second.erase(path);
Expand All @@ -113,13 +136,31 @@ UsdImaging_CollectionCache::RemoveCollection(UsdCollectionAPI const& c)
// This also reaps the associated identifier.
if (pathsForQueryEntry->second.empty()) {
_pathsForQuery.erase(pathsForQueryEntry);
_idForQuery.erase(queryRef);
_queryForId.erase(queryEntry);
TF_DEBUG(USDIMAGING_COLLECTIONS)
.Msg("UsdImaging_CollectionCache: Dropped id '%s'", id.GetText());
}
};

SdfPathSet const&
UsdImaging_CollectionCache::GetDirtyPaths() const
{
return _dirtyPaths;
}

bool
UsdImaging_CollectionCache::AreAllPathsDirty() const
{
return _allPathsDirty;
}

void
UsdImaging_CollectionCache::ClearDirtyPaths()
{
_dirtyPaths.clear();
_allPathsDirty = false;
}

TfToken
UsdImaging_CollectionCache::GetIdForCollection(UsdCollectionAPI const& c)
{
Expand Down
26 changes: 25 additions & 1 deletion pxr/usdImaging/usdImaging/collectionCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class UsdImaging_CollectionCache : boost::noncopyable {

/// Remove any cached entry for the given collection.
/// Does nothing if no cache entry exists.
void RemoveCollection(UsdCollectionAPI const& collection);
void RemoveCollection(UsdStageWeakPtr const& stage, SdfPath const& path);

/// Return the cached entry for the given collection.
TfToken
Expand All @@ -79,6 +79,24 @@ class UsdImaging_CollectionCache : boost::noncopyable {
VtArray<TfToken>
ComputeCollectionsContainingPath(SdfPath const& path) const;

/// Return the cached MembershipQuery for a given id
void
GetMembershipQuery(TfToken const& id, const Query** query) const;

/// Returns true if all paths should be considered dirty
/// This happens when a collection containing an exclusion is updated
bool
AreAllPathsDirty() const;

/// Returns a set of dirty paths
/// Should only be used if AreAllPathsDirty returned false
SdfPathSet const&
GetDirtyPaths() const;

/// Clears the internal dirty flags
void
ClearDirtyPaths();

private:
// The cache boils down to tracking the correspondence of
// collection paths, their computed queries, and the id
Expand All @@ -95,6 +113,12 @@ class UsdImaging_CollectionCache : boost::noncopyable {
std::unordered_map<SdfPath, TfToken, SdfPath::Hash> _idForPath;
std::unordered_map<Query, SdfPathSet, Query::Hash> _pathsForQuery;

void
_MarkCollectionContentDirty(UsdStageWeakPtr const& stage, UsdCollectionAPI::MembershipQuery const& query);

SdfPathSet _dirtyPaths;
bool _allPathsDirty = false;

std::mutex _mutex;
};

Expand Down
33 changes: 33 additions & 0 deletions pxr/usdImaging/usdImaging/delegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,39 @@ UsdImagingDelegate::ApplyPendingUpdates()
indexProxy._UniqueifyPathsToRepopulate();
_Populate(&indexProxy);
_ExecuteWorkForVariabilityUpdate(&worker);

// Mark all dirty collection prims
if (_collectionCache.AreAllPathsDirty())
{
TRACE_SCOPE("Mark all dirty collection members");
TF_FOR_ALL(it, _hdPrimInfoMap) {
UsdImagingPrimAdapterSharedPtr& adapter = it->second.adapter;
TF_DEBUG(USDIMAGING_CHANGES).Msg("[Update]: invalidate collection member prim (all paths dirty) %s\n", it->first.GetText());
adapter->MarkDirty(it->second.usdPrim, it->first,
aloysbaillet marked this conversation as resolved.
Show resolved Hide resolved
HdChangeTracker::DirtyCategories, &indexProxy);
}
_collectionCache.ClearDirtyPaths();
}
else
{
const SdfPathSet& pathsDirtiedByCollections = _collectionCache.GetDirtyPaths();
if(!pathsDirtiedByCollections.empty())
{
TRACE_SCOPE("Mark dirty collection members");
for(const SdfPath& dirtyPath : pathsDirtiedByCollections)
{
TF_DEBUG(USDIMAGING_CHANGES).Msg("[Update]: invalidate collection member prim %s\n", dirtyPath.GetText());
_HdPrimInfo* primInfo = _GetHdPrimInfo(dirtyPath);
if (primInfo && primInfo->usdPrim.IsValid() &&
TF_VERIFY(primInfo->adapter, "%s", dirtyPath.GetText())) {
UsdImagingPrimAdapterSharedPtr& adapter = primInfo->adapter;
adapter->MarkDirty(primInfo->usdPrim, dirtyPath,
HdChangeTracker::DirtyCategories, &indexProxy);
}
}
_collectionCache.ClearDirtyPaths();
}
}
}

void
Expand Down
6 changes: 6 additions & 0 deletions pxr/usdImaging/usdImaging/delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,12 @@ class UsdImagingDelegate : public HdSceneDelegate, public TfWeakBase {
USDIMAGING_API
bool IsInInvisedPaths(const SdfPath &usdPath) const;

USDIMAGING_API
aloysbaillet marked this conversation as resolved.
Show resolved Hide resolved
UsdStageRefPtr GetStage() const
{
return _stage;
}

private:
// Internal Get and SamplePrimvar
VtValue _Get(SdfPath const& id, TfToken const& key, VtIntArray *outIndices);
Expand Down
1 change: 1 addition & 0 deletions pxr/usdImaging/usdImaging/diskLightAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void
UsdImagingDiskLightAdapter::_RemovePrim(SdfPath const& cachePath,
UsdImagingIndexProxy* index)
{
UsdImagingLightAdapter::_RemovePrim(cachePath, index);
index->RemoveSprim(HdPrimTypeTokens->diskLight, cachePath);
}

Expand Down
1 change: 1 addition & 0 deletions pxr/usdImaging/usdImaging/distantLightAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void
UsdImagingDistantLightAdapter::_RemovePrim(SdfPath const& cachePath,
UsdImagingIndexProxy* index)
{
UsdImagingLightAdapter::_RemovePrim(cachePath, index);
index->RemoveSprim(HdPrimTypeTokens->distantLight, cachePath);
}

Expand Down
1 change: 1 addition & 0 deletions pxr/usdImaging/usdImaging/domeLightAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void
UsdImagingDomeLightAdapter::_RemovePrim(SdfPath const& cachePath,
UsdImagingIndexProxy* index)
{
UsdImagingLightAdapter::_RemovePrim(cachePath, index);
index->RemoveSprim(HdPrimTypeTokens->domeLight, cachePath);
}

Expand Down
52 changes: 47 additions & 5 deletions pxr/usdImaging/usdImaging/lightAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,42 @@ UsdImagingLightAdapter::_RemovePrim(SdfPath const& cachePath,
UsdImagingIndexProxy* index)
{
index->RemoveSprim(HdPrimTypeTokens->light, cachePath);
UsdImaging_CollectionCache &collectionCache = _GetCollectionCache();
SdfPath lightLinkPath = cachePath.AppendProperty(UsdImagingTokens->collectionLightLink);
collectionCache.RemoveCollection(GetDelegate()->GetStage(), lightLinkPath);
SdfPath shadowLinkPath = cachePath.AppendProperty(UsdImagingTokens->collectionShadowLink);
collectionCache.RemoveCollection(GetDelegate()->GetStage(), shadowLinkPath);
aloysbaillet marked this conversation as resolved.
Show resolved Hide resolved
}

bool
UsdImagingLightAdapter::_UpdateCollectionsChanged(UsdPrim const& prim, SdfPath const& cachePath) const
{
UsdImaging_CollectionCache &collectionCache = _GetCollectionCache();
auto getCollectionHash = [&collectionCache] (const UsdCollectionAPI& api) -> size_t {
const TfToken id = collectionCache.UpdateCollection(api);
const UsdImaging_CollectionCache::Query* query = nullptr;
collectionCache.GetMembershipQuery(id, &query);
return query != nullptr ? query->GetHash() : 0;
};
UsdLuxLight light(prim);
const size_t newLightCollectionHash = getCollectionHash(light.GetLightLinkCollectionAPI());
const size_t newShadowCollectionHash = getCollectionHash(light.GetShadowLinkCollectionAPI());
aloysbaillet marked this conversation as resolved.
Show resolved Hide resolved
auto hashesIt = _collectionHashes.find(cachePath);
if(hashesIt == _collectionHashes.end()){
hashesIt = _collectionHashes.insert({cachePath, {0, 0}}).first;
}
HashPair& hashes = hashesIt->second;
if (newLightCollectionHash != hashes.lightCollectionHash || newShadowCollectionHash != hashes.shadowCollectionHash)
{

hashes.lightCollectionHash = newLightCollectionHash;
hashes.shadowCollectionHash = newShadowCollectionHash;
return true;
}
else
{
return false;
}
}

void
Expand Down Expand Up @@ -127,11 +163,14 @@ UsdImagingLightAdapter::TrackVariability(UsdPrim const& prim,

UsdLuxLightAPI light(prim);
if (TF_VERIFY(light)) {
UsdImaging_CollectionCache &collectionCache = _GetCollectionCache();
collectionCache.UpdateCollection(light.GetLightLinkCollectionAPI());
collectionCache.UpdateCollection(light.GetShadowLinkCollectionAPI());
// TODO: When collections change we need to invalidate affected
// prims with the DirtyCollections flag.
if (_UpdateCollectionsChanged(prim, cachePath))
{
*timeVaryingBits |= HdLight::DirtyBits::DirtyCollection;
}
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can skip the else here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, can just call _UpdateCollectionsChanged. I don't think collections are allowed to be time-varying? And if you did want to check that, instead of seeing if the collection has new contents, you'd want to ask whether the attributes are time varying.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the call altogether as the attributes cannot be animated... I'm guessing that if timesamples are added on the includeRoot bool that could cause some issues though?

{
*timeVaryingBits &= ~HdLight::DirtyBits::DirtyCollection;
}
}

// XXX Cache primvars for lights.
Expand Down Expand Up @@ -177,6 +216,9 @@ UsdImagingLightAdapter::ProcessPropertyChange(UsdPrim const& prim,
if (UsdGeomXformable::IsTransformationAffectedByAttrNamed(propertyName)) {
return HdLight::DirtyBits::DirtyTransform;
}

_UpdateCollectionsChanged(prim, cachePath);
aloysbaillet marked this conversation as resolved.
Show resolved Hide resolved

// "DirtyParam" is the catch-all bit for light params.
return HdLight::DirtyBits::DirtyParams;
}
Expand Down
12 changes: 12 additions & 0 deletions pxr/usdImaging/usdImaging/lightAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ class UsdImagingLightAdapter : public UsdImagingPrimAdapter {
void _RemovePrim(SdfPath const& cachePath,
UsdImagingIndexProxy* index) override;

private:
/// Updates the collection cache content
/// Checks for collection hash change and returns true if they are different
bool _UpdateCollectionsChanged(UsdPrim const& prim, SdfPath const& cachePath) const;

// Cache to detect collection changes
struct HashPair{
size_t lightCollectionHash = 0;
size_t shadowCollectionHash = 0;
};
mutable std::unordered_map<SdfPath, HashPair, SdfPath::Hash> _collectionHashes;

};


Expand Down
1 change: 1 addition & 0 deletions pxr/usdImaging/usdImaging/rectLightAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ void
UsdImagingRectLightAdapter::_RemovePrim(SdfPath const& cachePath,
UsdImagingIndexProxy* index)
{
UsdImagingLightAdapter::_RemovePrim(cachePath, index);
index->RemoveSprim(HdPrimTypeTokens->rectLight, cachePath);
}

Expand Down
1 change: 1 addition & 0 deletions pxr/usdImaging/usdImaging/sphereLightAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ void
UsdImagingSphereLightAdapter::_RemovePrim(SdfPath const& cachePath,
UsdImagingIndexProxy* index)
{
UsdImagingLightAdapter::_RemovePrim(cachePath, index);
index->RemoveSprim(HdPrimTypeTokens->sphereLight, cachePath);
}

Expand Down
2 changes: 2 additions & 0 deletions pxr/usdImaging/usdImaging/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ PXR_NAMESPACE_OPEN_SCOPE


#define USDIMAGING_TOKENS \
((collectionLightLink, "collection:lightLink")) \
((collectionShadowLink, "collection:shadowLink")) \
((infoSource, "info:source")) \
(faceIndexPrimvar) \
(faceOffsetPrimvar) \
Expand Down