Skip to content

Commit 3ca84cf

Browse files
committed
Refactor base rprim to propagate the mesh fix
1 parent 996f77f commit 3ca84cf

7 files changed

Lines changed: 439 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: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,17 @@
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 <limits>
2535

2636
#ifdef MAYA_HAS_DISPLAY_LAYER_API
2737
#include <mayaUsd/utils/util.h>
@@ -405,6 +415,12 @@ HdReprSharedPtr MayaUsdRPrim::_InitReprCommon(
405415

406416
void MayaUsdRPrim::_PropagateDirtyBitsCommon(HdDirtyBits& bits, const ReprVector& reprs) const
407417
{
418+
// This supports UsdSkel affecting the points position when the transform is dirty.
419+
// Done before the propagation below so the draw items see the added DirtyPoints too.
420+
if (bits & HdChangeTracker::DirtyTransform && _pointsFromSkel) {
421+
bits |= HdChangeTracker::DirtyPoints;
422+
}
423+
408424
if (bits & HdChangeTracker::AllDirty) {
409425
// RPrim is dirty, propagate dirty bits to all draw items.
410426
RenderItemFunc setDirtyBitsToItem = [bits](HdVP2DrawItem::RenderItemData& renderItemData) {
@@ -679,6 +695,81 @@ void MayaUsdRPrim::_UpdatePrimvarSourcesGeneric(
679695
}
680696
}
681697

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

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