Skip to content

Commit f3da838

Browse files
authored
Merge pull request #4693 from Autodesk/barbalt/dev/EMSUSD-1695-Curves-not-deformed-viewport
EMSUSD-1695 USDSkel: Curves are not deformed in Maya viewport
2 parents fd2922e + a2385cc commit f3da838

7 files changed

Lines changed: 397 additions & 154 deletions

File tree

lib/mayaUsd/render/vp2RenderDelegate/basisCurves.cpp

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
#include <maya/MProfiler.h>
4040
#include <maya/MSelectionMask.h>
4141

42+
#include <algorithm>
43+
4244
PXR_NAMESPACE_OPEN_SCOPE
4345

4446
namespace {
@@ -434,13 +436,20 @@ void HdVP2BasisCurves::Sync(
434436
#endif
435437

436438
const SdfPath& id = GetId();
437-
if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->normals)
439+
if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->points)
440+
|| HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->normals)
438441
|| HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->primvar)) {
439442
TfTokenVector requiredPrimvars;
440443
if (!_GetMaterialPrimvars(renderIndex, GetMaterialId(), requiredPrimvars)) {
441444
requiredPrimvars = sFallbackShaderPrimvars;
442445
}
443446

447+
// Points are always required. They are normally read straight from the scene
448+
// delegate below, but they may instead be the output of an HdExtComputation
449+
// (that is how UsdSkel supplies skinned points), which _UpdatePrimvarSources
450+
// resolves into the primvar source map.
451+
_RequirePrimvar(requiredPrimvars, HdTokens->points);
452+
444453
_UpdatePrimvarSources(delegate, *dirtyBits, requiredPrimvars);
445454
}
446455

@@ -455,50 +464,20 @@ void HdVP2BasisCurves::Sync(
455464
// Prepare position buffer. It is shared among all draw items so it should
456465
// be updated only once when it gets dirty.
457466
if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->points)) {
458-
const VtValue value = delegate->Get(id, HdTokens->points);
459-
_curvesSharedData._points = value.Get<VtVec3fArray>();
460-
461-
const size_t numVertices = _curvesSharedData._points.size();
467+
_curvesSharedData._points
468+
= _ResolvePoints(delegate, id, _curvesSharedData._primvarSourceMap).Get<VtVec3fArray>();
462469

463470
const HdBasisCurvesTopology& topology = _curvesSharedData._topology;
464471
const size_t numControlPoints = topology.CalculateNeededNumberOfControlPoints();
465472

466-
if (!topology.HasIndices() && numVertices != numControlPoints) {
473+
if (!topology.HasIndices() && _curvesSharedData._points.size() != numControlPoints) {
467474
TF_WARN("Topology and vertices do not match for BasisCurve %s", id.GetName().c_str());
468475
}
469476

470-
void* bufferData = _curvesSharedData._positionsBuffer->acquire(numVertices, true);
471-
if (bufferData) {
472-
const size_t numBytes = sizeof(GfVec3f) * numVertices;
473-
memcpy(bufferData, _curvesSharedData._points.cdata(), numBytes);
474-
475-
// Capture class member for lambda
476-
MHWRender::MVertexBuffer* const positionsBuffer
477-
= _curvesSharedData._positionsBuffer.get();
478-
const MString& rprimId = _rprimId;
479-
480-
_delegate->GetVP2ResourceRegistry().EnqueueCommit(
481-
[positionsBuffer, bufferData, rprimId]() {
482-
MProfilingScope profilingScope(
483-
HdVP2RenderDelegate::sProfilerCategory,
484-
MProfiler::kColorC_L2,
485-
rprimId.asChar(),
486-
"CommitPositions");
487-
488-
positionsBuffer->commit(bufferData);
489-
});
490-
}
477+
_CommitPositionsBuffer(_curvesSharedData._positionsBuffer.get(), _curvesSharedData._points);
491478
}
492479

493-
#if PXR_VERSION > 2111
494-
const TfToken& renderTag = GetRenderTag();
495-
#else
496-
const TfToken& renderTag = delegate->GetRenderTag(id);
497-
#endif
498-
499-
_SyncSharedData(_sharedData, delegate, dirtyBits, reprToken, *this, _reprs, renderTag);
500-
501-
*dirtyBits = HdChangeTracker::Clean;
480+
_SyncEndCommon(*this, delegate, dirtyBits, reprToken, _sharedData, _reprs);
502481

503482
// Draw item update is controlled by its own dirty bits.
504483
_UpdateRepr(delegate, reprToken);
@@ -1418,6 +1397,11 @@ void HdVP2BasisCurves::_UpdatePrimvarSources(
14181397

14191398
_UpdatePrimvarSourcesGeneric(
14201399
sceneDelegate, dirtyBits, requiredPrimvars, *this, updatePrimvarInfo, erasePrimvarInfo);
1400+
1401+
// At this point we've searched for the required primvars. Check to see if
1402+
// there are any HdExtComputation which should replace primvar data or fill in for a
1403+
// missing primvar. This is what makes UsdSkel-skinned curves deform.
1404+
_UpdateComputedPrimvarSourcesGeneric(sceneDelegate, requiredPrimvars, *this, updatePrimvarInfo);
14211405
}
14221406

14231407
/*! \brief Create render item for smoothHull repr.

lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.cpp

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,18 @@
2121
#include "renderDelegate.h"
2222
#include "tokens.h"
2323

24+
#include <pxr/imaging/hd/extComputation.h>
25+
#include <pxr/imaging/hd/version.h>
2426
#include <pxr/usdImaging/usdImaging/delegate.h>
27+
#if !defined(HD_API_VERSION) || HD_API_VERSION < 49
28+
#include <pxr/imaging/hd/extCompCpuComputation.h>
29+
#else
30+
#include <pxr/imaging/hdSt/extCompCpuComputation.h>
31+
#endif
32+
33+
#include <algorithm>
34+
#include <cstring>
35+
#include <limits>
2536

2637
#ifdef MAYA_HAS_DISPLAY_LAYER_API
2738
#include <mayaUsd/utils/util.h>
@@ -405,6 +416,12 @@ HdReprSharedPtr MayaUsdRPrim::_InitReprCommon(
405416

406417
void MayaUsdRPrim::_PropagateDirtyBitsCommon(HdDirtyBits& bits, const ReprVector& reprs) const
407418
{
419+
// This supports UsdSkel affecting the points position when the transform is dirty.
420+
// Done before the propagation below so the draw items see the added DirtyPoints too.
421+
if (bits & HdChangeTracker::DirtyTransform && _pointsFromSkel) {
422+
bits |= HdChangeTracker::DirtyPoints;
423+
}
424+
408425
if (bits & HdChangeTracker::AllDirty) {
409426
// RPrim is dirty, propagate dirty bits to all draw items.
410427
RenderItemFunc setDirtyBitsToItem = [bits](HdVP2DrawItem::RenderItemData& renderItemData) {
@@ -679,6 +696,84 @@ void MayaUsdRPrim::_UpdatePrimvarSourcesGeneric(
679696
}
680697
}
681698

699+
void MayaUsdRPrim::_UpdateComputedPrimvarSourcesGeneric(
700+
HdSceneDelegate* sceneDelegate,
701+
const TfTokenVector& requiredPrimvars,
702+
HdRprim& refThis,
703+
UpdatePrimvarInfoFunc& updatePrimvarInfo)
704+
{
705+
#if !defined(HD_API_VERSION) || HD_API_VERSION < 49
706+
using HdStExtCompCpuComputation = HdExtCompCpuComputation;
707+
using HdStExtCompCpuComputationSharedPtr = HdExtCompCpuComputationSharedPtr;
708+
#endif
709+
710+
// Recomputed from scratch on every call, so that a prim which stops being skinned
711+
// (its binding removed, say) goes back to reading the authored points.
712+
_pointsFromSkel = false;
713+
714+
const SdfPath& id = refThis.GetId();
715+
716+
// The compPrimvars are a description of the link between the compute system and
717+
// what we need to draw.
718+
HdExtComputationPrimvarDescriptorVector compPrimvars
719+
= sceneDelegate->GetExtComputationPrimvarDescriptors(id, HdInterpolationVertex);
720+
if (compPrimvars.empty()) {
721+
return;
722+
}
723+
724+
const HdRenderIndex& renderIndex = sceneDelegate->GetRenderIndex();
725+
726+
for (const auto& primvarName : requiredPrimvars) {
727+
auto result
728+
= std::find_if(compPrimvars.begin(), compPrimvars.end(), [&](const auto& compPrimvar) {
729+
return compPrimvar.name == primvarName;
730+
});
731+
// if there is no compute for the given required primvar then we're done!
732+
if (result == compPrimvars.end())
733+
continue;
734+
HdExtComputationPrimvarDescriptor compPrimvar = *result;
735+
736+
// The compPrimvar has the Id of the compute the data comes from, and the output
737+
// of the compute which contains the data.
738+
HdExtComputation const* sourceComp
739+
= static_cast<HdExtComputation const*>(renderIndex.GetSprim(
740+
HdPrimTypeTokens->extComputation, compPrimvar.sourceComputationId));
741+
if (!sourceComp || sourceComp->GetElementCount() <= 0)
742+
continue;
743+
744+
// Create the HdStExtCompCpuComputation objects necessary to resolve the computation.
745+
HdStExtCompCpuComputationSharedPtr cpuComputation;
746+
HdBufferSourceSharedPtrVector sources;
747+
// There is a possible data race calling CreateComputation, see
748+
// https://github.com/PixarAnimationStudios/USD/issues/1742
749+
cpuComputation
750+
= HdStExtCompCpuComputation::CreateComputation(sceneDelegate, *sourceComp, &sources);
751+
752+
// Immediately resolve the computation so we can fill in the primvar info.
753+
for (HdBufferSourceSharedPtr& source : sources) {
754+
source->Resolve();
755+
}
756+
757+
// Pull the result out of the compute and save it into the local primvar info.
758+
size_t outputIndex
759+
= cpuComputation->GetOutputIndex(compPrimvar.sourceComputationOutputName);
760+
// INVALID_OUTPUT_INDEX is declared static in USD, can't access here so re-declare
761+
constexpr size_t INVALID_OUTPUT_INDEX = std::numeric_limits<size_t>::max();
762+
if (INVALID_OUTPUT_INDEX == outputIndex) {
763+
// Don't make the points dirty
764+
continue;
765+
}
766+
767+
updatePrimvarInfo(
768+
primvarName, cpuComputation->GetOutputByIndex(outputIndex), HdInterpolationVertex);
769+
770+
// Record that the points are coming from the computation.
771+
if (primvarName == HdTokens->points) {
772+
_pointsFromSkel = true;
773+
}
774+
}
775+
}
776+
682777
#ifdef MAYA_HAS_DISPLAY_LAYER_API
683778
void MayaUsdRPrim::_ProcessDisplayLayerModes(
684779
const MObject& displayLayerObj,
@@ -838,6 +933,64 @@ void MayaUsdRPrim::_SyncDisplayLayerModesInstanced(SdfPath const& id, unsigned i
838933
}
839934
}
840935

936+
void MayaUsdRPrim::_SyncEndCommon(
937+
HdRprim& refThis,
938+
HdSceneDelegate* delegate,
939+
HdDirtyBits* dirtyBits,
940+
TfToken const& reprToken,
941+
HdRprimSharedData& sharedData,
942+
ReprVector const& reprs)
943+
{
944+
#if PXR_VERSION > 2111
945+
const TfToken& renderTag = refThis.GetRenderTag();
946+
#else
947+
const TfToken& renderTag = delegate->GetRenderTag(refThis.GetId());
948+
#endif
949+
950+
_SyncSharedData(sharedData, delegate, dirtyBits, reprToken, refThis, reprs, renderTag);
951+
952+
*dirtyBits = HdChangeTracker::Clean;
953+
}
954+
955+
void MayaUsdRPrim::_RequirePrimvar(TfTokenVector& requiredPrimvars, const TfToken& primvar)
956+
{
957+
if (std::find(requiredPrimvars.cbegin(), requiredPrimvars.cend(), primvar)
958+
== requiredPrimvars.cend()) {
959+
requiredPrimvars.push_back(primvar);
960+
}
961+
}
962+
963+
void MayaUsdRPrim::_CommitPositionsBuffer(
964+
MHWRender::MVertexBuffer* positionsBuffer,
965+
const VtVec3fArray& points) const
966+
{
967+
if (!positionsBuffer) {
968+
return;
969+
}
970+
971+
const size_t numVertices = points.size();
972+
973+
void* bufferData = positionsBuffer->acquire(numVertices, true);
974+
if (!bufferData) {
975+
return;
976+
}
977+
978+
memcpy(bufferData, points.cdata(), sizeof(GfVec3f) * numVertices);
979+
980+
// Capture class member for lambda
981+
const MString& rprimId = _rprimId;
982+
983+
_delegate->GetVP2ResourceRegistry().EnqueueCommit([positionsBuffer, bufferData, rprimId]() {
984+
MProfilingScope profilingScope(
985+
HdVP2RenderDelegate::sProfilerCategory,
986+
MProfiler::kColorC_L2,
987+
rprimId.asChar(),
988+
"CommitPositions");
989+
990+
positionsBuffer->commit(bufferData);
991+
});
992+
}
993+
841994
void MayaUsdRPrim::_SyncSharedData(
842995
HdRprimSharedData& sharedData,
843996
HdSceneDelegate* delegate,

lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,42 @@ class MayaUsdRPrim
302302
ReprVector const& reprs,
303303
TfToken const& renderTag);
304304

305+
//! Shared tail of an rprim's Sync(): resolve the render tag, sync the shared data and
306+
//! clear the dirty bits.
307+
void _SyncEndCommon(
308+
HdRprim& refThis,
309+
HdSceneDelegate* delegate,
310+
HdDirtyBits* dirtyBits,
311+
TfToken const& reprToken,
312+
HdRprimSharedData& sharedData,
313+
ReprVector const& reprs);
314+
315+
//! Append \p primvar to \p requiredPrimvars unless it is already there.
316+
static void _RequirePrimvar(TfTokenVector& requiredPrimvars, const TfToken& primvar);
317+
318+
//! Return the points to render. These normally come straight from the scene delegate,
319+
//! but for a UsdSkel-skinned prim the delegate reports the undeformed rest points and
320+
//! the deformed ones are the output of an HdExtComputation, which
321+
template <typename PrimvarSourceMap>
322+
VtValue _ResolvePoints(
323+
HdSceneDelegate* delegate,
324+
const SdfPath& id,
325+
const PrimvarSourceMap& primvarSourceMap) const
326+
{
327+
if (_pointsFromSkel) {
328+
const auto it = primvarSourceMap.find(HdTokens->points);
329+
if (it != primvarSourceMap.end()) {
330+
return it->second.data;
331+
}
332+
}
333+
return delegate->Get(id, HdTokens->points);
334+
}
335+
336+
//! Copy \p points into \p positionsBuffer and enqueue the commit of that buffer.
337+
void _CommitPositionsBuffer(
338+
MHWRender::MVertexBuffer* positionsBuffer,
339+
const VtVec3fArray& points) const;
340+
305341
void _SyncDisplayLayerModes(SdfPath const& id, bool instancedPrim);
306342
void _SyncDisplayLayerModesInstanced(SdfPath const& id, unsigned int instanceCount);
307343

@@ -329,6 +365,19 @@ class MayaUsdRPrim
329365
UpdatePrimvarInfoFunc& updatePrimvarInfo,
330366
ErasePrimvarInfoFunc& erasePrimvarInfo);
331367

368+
//! Resolve any HdExtComputation which supplies one of \p requiredPrimvars, and feed the
369+
//! computed value back through \p updatePrimvarInfo, replacing the authored primvar.
370+
//! This is how UsdSkel delivers deformed points to the render delegate: UsdImaging
371+
//! publishes the rest points as the authored primvar and the skinned points as the
372+
//! output of a computation.
373+
//! Also refreshes _pointsFromSkel, so every rprim that calls this automatically gets
374+
//! the transform-dirtying behaviour _PropagateDirtyBitsCommon() applies.
375+
void _UpdateComputedPrimvarSourcesGeneric(
376+
HdSceneDelegate* sceneDelegate,
377+
const TfTokenVector& requiredPrimvars,
378+
HdRprim& refThis,
379+
UpdatePrimvarInfoFunc& updatePrimvarInfo);
380+
332381
SdfPath _GetUpdatedMaterialId(HdRprim* rprim, HdSceneDelegate* delegate);
333382
MColor _GetHighlightColor(const TfToken& className);
334383
MColor _GetHighlightColor(const TfToken& className, HdVP2SelectionStatus selectionStatus);
@@ -388,6 +437,10 @@ class MayaUsdRPrim
388437
//! Selection status of the Rprim
389438
HdVP2SelectionStatus _selectionStatus { kUnselected };
390439

440+
//! Record if the points positions are generated by a UsdSkel, i.e. come from an
441+
//! HdExtComputation rather than from the authored points primvar.
442+
bool _pointsFromSkel { false };
443+
391444
//! Modes requested by display layer along with the frame they are updated on
392445
bool _useInstancedDisplayLayerModes { false };
393446
DisplayLayerModes _displayLayerModes;

0 commit comments

Comments
 (0)