Skip to content

Commit

Permalink
Merge pull request #2972 from Autodesk/gamaj/smart_signaling
Browse files Browse the repository at this point in the history
Add smart signaling for renderers.
  • Loading branch information
seando-adsk authored Mar 28, 2023
2 parents 6e6a9de + 98fe843 commit cc4a36a
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/mayaUsd/nodes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ target_sources(${PROJECT_NAME}
proxyShapeBase.cpp
proxyShapePlugin.cpp
proxyShapeStageExtraData.cpp
proxyShapeUpdateManager.cpp
stageData.cpp
stageNode.cpp
usdPrimProvider.cpp
Expand All @@ -22,6 +23,7 @@ set(HEADERS
proxyShapePlugin.h
proxyStageProvider.h
proxyShapeStageExtraData.h
proxyShapeUpdateManager.h
stageData.h
stageNode.h
usdPrimProvider.h
Expand Down
44 changes: 44 additions & 0 deletions lib/mayaUsd/nodes/proxyShapeBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ MObject MayaUsdProxyShapeBase::drawGuidePurposeAttr;
MObject MayaUsdProxyShapeBase::sessionLayerNameAttr;
MObject MayaUsdProxyShapeBase::rootLayerNameAttr;
MObject MayaUsdProxyShapeBase::mutedLayersAttr;
// Change counter attributes
MObject MayaUsdProxyShapeBase::updateCounterAttr;
MObject MayaUsdProxyShapeBase::resyncCounterAttr;
// Output attributes
MObject MayaUsdProxyShapeBase::outTimeAttr;
MObject MayaUsdProxyShapeBase::outStageDataAttr;
Expand Down Expand Up @@ -388,6 +391,26 @@ MStatus MayaUsdProxyShapeBase::initialize()
retValue = addAttribute(outStageDataAttr);
CHECK_MSTATUS_AND_RETURN_IT(retValue);

updateCounterAttr
= numericAttrFn.create("updateId", "upid", MFnNumericData::kInt64, -1, &retValue);
CHECK_MSTATUS_AND_RETURN_IT(retValue);
numericAttrFn.setStorable(false);
numericAttrFn.setWritable(false);
numericAttrFn.setHidden(true);
numericAttrFn.setInternal(true);
retValue = addAttribute(updateCounterAttr);
CHECK_MSTATUS_AND_RETURN_IT(retValue);

resyncCounterAttr
= numericAttrFn.create("resyncId", "rsid", MFnNumericData::kInt64, -1, &retValue);
CHECK_MSTATUS_AND_RETURN_IT(retValue);
numericAttrFn.setStorable(false);
numericAttrFn.setWritable(false);
numericAttrFn.setHidden(true);
numericAttrFn.setInternal(true);
retValue = addAttribute(resyncCounterAttr);
CHECK_MSTATUS_AND_RETURN_IT(retValue);

outStageCacheIdAttr
= numericAttrFn.create("outStageCacheId", "ostcid", MFnNumericData::kInt, -1, &retValue);
CHECK_MSTATUS_AND_RETURN_IT(retValue);
Expand Down Expand Up @@ -524,6 +547,22 @@ void MayaUsdProxyShapeBase::postConstructor()
MayaUsdProxyStageInvalidateNotice(*this).Send();
}

/* virtual */
bool MayaUsdProxyShapeBase::getInternalValue(const MPlug& plug, MDataHandle& handle)
{
bool retVal = true;

if (plug == updateCounterAttr) {
handle.set(_shapeUpdateManager.GetUpdateCount());
} else if (plug == resyncCounterAttr) {
handle.set(_shapeUpdateManager.GetResyncCount());
} else {
retVal = MPxSurfaceShape::getInternalValue(plug, handle);
}

return retVal;
}

/* virtual */
MStatus MayaUsdProxyShapeBase::compute(const MPlug& plug, MDataBlock& dataBlock)
{
Expand Down Expand Up @@ -1936,6 +1975,11 @@ void MayaUsdProxyShapeBase::_OnStageObjectsChanged(const UsdNotice::ObjectsChang
ProxyAccessor::stageChanged(_usdAccessor, thisMObject(), notice);
MayaUsdProxyStageObjectsChangedNotice(*this, notice).Send();

// Also keeps track of the notification counters:
if (_shapeUpdateManager.CanIgnoreObjectsChanged(notice)) {
return;
}

// Recompute the extents of any UsdGeomBoundable that has authored extents
const auto& stage = notice.GetStage();
if (stage != getUsdStage()) {
Expand Down
12 changes: 12 additions & 0 deletions lib/mayaUsd/nodes/proxyShapeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ constexpr char USD_UFE_SEPARATOR = '/';
#include <mayaUsd/base/api.h>
#include <mayaUsd/listeners/stageNoticeListener.h>
#include <mayaUsd/nodes/proxyAccessor.h>
#include <mayaUsd/nodes/proxyShapeUpdateManager.h>
#include <mayaUsd/nodes/proxyStageProvider.h>
#include <mayaUsd/nodes/usdPrimProvider.h>

Expand Down Expand Up @@ -124,6 +125,12 @@ class MayaUsdProxyShapeBase
MAYAUSD_CORE_PUBLIC
static MObject mutedLayersAttr;

// Change counter attributes
MAYAUSD_CORE_PUBLIC
static MObject updateCounterAttr;
MAYAUSD_CORE_PUBLIC
static MObject resyncCounterAttr;

// Output attributes
MAYAUSD_CORE_PUBLIC
static MObject outTimeAttr;
Expand Down Expand Up @@ -170,6 +177,8 @@ class MayaUsdProxyShapeBase
MAYAUSD_CORE_PUBLIC
void postConstructor() override;
MAYAUSD_CORE_PUBLIC
bool getInternalValue(const MPlug&, MDataHandle&) override;
MAYAUSD_CORE_PUBLIC
MStatus compute(const MPlug& plug, MDataBlock& dataBlock) override;
MAYAUSD_CORE_PUBLIC
bool isBounded() const override;
Expand Down Expand Up @@ -398,6 +407,9 @@ class MayaUsdProxyShapeBase
size_t _excludePrimPathsVersion { 1 };
size_t _UsdStageVersion { 1 };

// This helper class keeps track of the notification counters:
MayaUsdProxyShapeUpdateManager _shapeUpdateManager;

MayaUsd::ProxyAccessor::Owner _usdAccessor;

static ClosestPointDelegate _sharedClosestPointDelegate;
Expand Down
156 changes: 156 additions & 0 deletions lib/mayaUsd/nodes/proxyShapeUpdateManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//
// Copyright 2023 Autodesk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "proxyShapeUpdateManager.h"

#include <pxr/base/vt/value.h>
#include <pxr/usd/sdf/listOp.h>
#include <pxr/usd/usd/tokens.h>
#include <pxr/usd/usdUI/tokens.h>

using namespace MAYAUSD_NS_DEF;

PXR_NAMESPACE_OPEN_SCOPE

namespace {

// We have incoming changes that USD will consider either requiring an
// update (meaning the render delegate needs to refresh and redraw) or
// a resync (meaning the scene delegate needs to fetch new datum). We
// want external clients to be aware of these classes of updates in case
// they do not use the Hydra system for refreshing and drawing the scene.
enum class _UsdChangeType
{
kIgnored, // Change does not require redraw: UI change, metadata change.
kUpdate, // Change requires redraw after refreshing parameter values
kResync // Change requires refreshing cached buffers
};

// If the notification is about prepending a UI schema, we don't want a refresh. These structures
// are quite large to inspect, but they hash easily, so let's compare known hashes.
bool _IsUiSchemaPrepend(const VtValue& v)
{
static std::set<size_t> UiSchemaPrependHashes;
std::once_flag hasHashes;
std::call_once(hasHashes, [&]() {
SdfTokenListOp op;
op.SetPrependedItems(TfTokenVector { TfToken("NodeGraphNodeAPI") });
UiSchemaPrependHashes.insert(hash_value(op));
});

if (v.IsHolding<SdfTokenListOp>()) {
const size_t hash = hash_value(v.UncheckedGet<SdfTokenListOp>());
if (UiSchemaPrependHashes.count(hash)) {
return true;
}
}
return false;
}

// This is a stripped down copy of UsdImagingDelegate::_OnUsdObjectsChanged which is the main USD
// notification handler where paths to refresh and paths to update are compiled for the next Hydra
// refresh. We do not gather paths as there is no simple way to know when to flush these maps.
//
// This needs to stay as quick as possible since it is stuck in the middle of the notification code
// path.
//
// This is a work in progress. Some improvements might be necessary in the future. The following
// potential issues are already visible:
//
// - Changing a parameter value for the first time creates the attribute, which is a kResync
_UsdChangeType _ClassifyUsdObjectsChanged(UsdNotice::ObjectsChanged const& notice)
{
using PathRange = UsdNotice::ObjectsChanged::PathRange;

auto range = notice.GetResyncedPaths();
if (!range.empty()) {
size_t ignoredCount = 0;
size_t resyncCount = 0;
for (auto it = range.begin(); it != range.end(); ++it) {
if (it->IsPropertyPath()) {
// We have a bunch of UI properties to ignore. Especially anything that comes from
// UI schemas.
if (it->GetName().rfind("ui:", 0) == 0) {
++ignoredCount;
continue;
}
}
for (const SdfChangeList::Entry* entry : it.base()->second) {
for (auto&& infoChanged : entry->infoChanged) {
if (infoChanged.first == UsdTokens->apiSchemas
&& _IsUiSchemaPrepend(infoChanged.second.second)) {
++ignoredCount;
} else {
++resyncCount;
}
}
}
}

if (ignoredCount) {
return resyncCount ? _UsdChangeType::kResync : _UsdChangeType::kIgnored;
} else {
return _UsdChangeType::kResync;
}
}

auto retVal = _UsdChangeType::kIgnored;

const PathRange pathsToUpdate = notice.GetChangedInfoOnlyPaths();
for (PathRange::const_iterator it = pathsToUpdate.begin(); it != pathsToUpdate.end(); ++it) {
if (it->IsAbsoluteRootOrPrimPath()) {
const TfTokenVector changedFields = it.GetChangedFields();
if (!changedFields.empty()) {
retVal = _UsdChangeType::kUpdate;
}
} else if (it->IsPropertyPath()) {
// We have a bunch of UI properties to ignore. Especially anything that comes from UI
// schemas.
if (it->GetName().rfind("ui:", 0) == 0) {
continue;
}
retVal = _UsdChangeType::kUpdate;
for (const auto& entry : it.base()->second) {
if (entry->flags.didChangeAttributeConnection) {
retVal = _UsdChangeType::kResync;
break;
}
}
}
}

return retVal;
}

} // namespace

bool MayaUsdProxyShapeUpdateManager::CanIgnoreObjectsChanged(
const UsdNotice::ObjectsChanged& notice)
{
switch (_ClassifyUsdObjectsChanged(notice)) {
case _UsdChangeType::kIgnored: return true;
case _UsdChangeType::kResync: ++_UsdStageResyncCounter;
// [[fallthrough]]; // We want that fallthrough to have the update always triggered.
case _UsdChangeType::kUpdate: ++_UsdStageUpdateCounter; break;
}

return false;
}

MInt64 MayaUsdProxyShapeUpdateManager::GetUpdateCount() { return _UsdStageUpdateCounter; }

MInt64 MayaUsdProxyShapeUpdateManager::GetResyncCount() { return _UsdStageResyncCounter; }

PXR_NAMESPACE_CLOSE_SCOPE
47 changes: 47 additions & 0 deletions lib/mayaUsd/nodes/proxyShapeUpdateManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright 2023 Autodesk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef PXRUSDMAYA_PROXY_SHAPE_UPDATE_MANAGER_H
#define PXRUSDMAYA_PROXY_SHAPE_UPDATE_MANAGER_H

#include <mayaUsd/base/api.h>

#include <pxr/pxr.h>
#include <pxr/usd/usd/notice.h>

#include <maya/MStatus.h>

PXR_NAMESPACE_OPEN_SCOPE

class MayaUsdProxyShapeUpdateManager
{
public:
MAYAUSD_CORE_PUBLIC
bool CanIgnoreObjectsChanged(const UsdNotice::ObjectsChanged& notice);

MAYAUSD_CORE_PUBLIC
MInt64 GetUpdateCount();

MAYAUSD_CORE_PUBLIC
MInt64 GetResyncCount();

private:
MInt64 _UsdStageUpdateCounter { 1 };
MInt64 _UsdStageResyncCounter { 1 };
};

PXR_NAMESPACE_CLOSE_SCOPE

#endif
Loading

0 comments on commit cc4a36a

Please sign in to comment.