Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
56 changes: 20 additions & 36 deletions lib/mayaUsd/render/vp2RenderDelegate/basisCurves.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include <maya/MProfiler.h>
#include <maya/MSelectionMask.h>

#include <algorithm>

PXR_NAMESPACE_OPEN_SCOPE

namespace {
Expand Down Expand Up @@ -434,13 +436,20 @@ void HdVP2BasisCurves::Sync(
#endif

const SdfPath& id = GetId();
if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->normals)
if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->points)
|| HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->normals)
|| HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->primvar)) {
TfTokenVector requiredPrimvars;
if (!_GetMaterialPrimvars(renderIndex, GetMaterialId(), requiredPrimvars)) {
requiredPrimvars = sFallbackShaderPrimvars;
}

// Points are always required. They are normally read straight from the scene
// delegate below, but they may instead be the output of an HdExtComputation
// (that is how UsdSkel supplies skinned points), which _UpdatePrimvarSources
// resolves into the primvar source map.
_RequirePrimvar(requiredPrimvars, HdTokens->points);

_UpdatePrimvarSources(delegate, *dirtyBits, requiredPrimvars);
}

Expand All @@ -455,50 +464,20 @@ void HdVP2BasisCurves::Sync(
// Prepare position buffer. It is shared among all draw items so it should
// be updated only once when it gets dirty.
if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->points)) {
const VtValue value = delegate->Get(id, HdTokens->points);
_curvesSharedData._points = value.Get<VtVec3fArray>();

const size_t numVertices = _curvesSharedData._points.size();
_curvesSharedData._points
= _ResolvePoints(delegate, id, _curvesSharedData._primvarSourceMap).Get<VtVec3fArray>();

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

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

void* bufferData = _curvesSharedData._positionsBuffer->acquire(numVertices, true);
if (bufferData) {
const size_t numBytes = sizeof(GfVec3f) * numVertices;
memcpy(bufferData, _curvesSharedData._points.cdata(), numBytes);

// Capture class member for lambda
MHWRender::MVertexBuffer* const positionsBuffer
= _curvesSharedData._positionsBuffer.get();
const MString& rprimId = _rprimId;

_delegate->GetVP2ResourceRegistry().EnqueueCommit(
[positionsBuffer, bufferData, rprimId]() {
MProfilingScope profilingScope(
HdVP2RenderDelegate::sProfilerCategory,
MProfiler::kColorC_L2,
rprimId.asChar(),
"CommitPositions");

positionsBuffer->commit(bufferData);
});
}
_CommitPositionsBuffer(_curvesSharedData._positionsBuffer.get(), _curvesSharedData._points);
}

#if PXR_VERSION > 2111
const TfToken& renderTag = GetRenderTag();
#else
const TfToken& renderTag = delegate->GetRenderTag(id);
#endif

_SyncSharedData(_sharedData, delegate, dirtyBits, reprToken, *this, _reprs, renderTag);

*dirtyBits = HdChangeTracker::Clean;
_SyncEndCommon(*this, delegate, dirtyBits, reprToken, _sharedData, _reprs);

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

_UpdatePrimvarSourcesGeneric(
sceneDelegate, dirtyBits, requiredPrimvars, *this, updatePrimvarInfo, erasePrimvarInfo);

// At this point we've searched for the required primvars. Check to see if
// there are any HdExtComputation which should replace primvar data or fill in for a
// missing primvar. This is what makes UsdSkel-skinned curves deform.
_UpdateComputedPrimvarSourcesGeneric(sceneDelegate, requiredPrimvars, *this, updatePrimvarInfo);
}

/*! \brief Create render item for smoothHull repr.
Expand Down
149 changes: 149 additions & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@
#include "renderDelegate.h"
#include "tokens.h"

#include <pxr/imaging/hd/extComputation.h>
#include <pxr/imaging/hd/version.h>
#include <pxr/usdImaging/usdImaging/delegate.h>
#if !defined(HD_API_VERSION) || HD_API_VERSION < 49
#include <pxr/imaging/hd/extCompCpuComputation.h>
#else
#include <pxr/imaging/hdSt/extCompCpuComputation.h>
#endif

#include <algorithm>
#include <limits>

Comment on lines +33 to 36
#ifdef MAYA_HAS_DISPLAY_LAYER_API
#include <mayaUsd/utils/util.h>
Expand Down Expand Up @@ -405,6 +415,12 @@

void MayaUsdRPrim::_PropagateDirtyBitsCommon(HdDirtyBits& bits, const ReprVector& reprs) const
{
// This supports UsdSkel affecting the points position when the transform is dirty.
// Done before the propagation below so the draw items see the added DirtyPoints too.
if (bits & HdChangeTracker::DirtyTransform && _pointsFromSkel) {
bits |= HdChangeTracker::DirtyPoints;
}

if (bits & HdChangeTracker::AllDirty) {
// RPrim is dirty, propagate dirty bits to all draw items.
RenderItemFunc setDirtyBitsToItem = [bits](HdVP2DrawItem::RenderItemData& renderItemData) {
Expand Down Expand Up @@ -679,6 +695,81 @@
}
}

void MayaUsdRPrim::_UpdateComputedPrimvarSourcesGeneric(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This function is almost a 1 to 1, from what mesh.cpp had. It was moved here so that it can be used by the other shapes (points and curves).

HdSceneDelegate* sceneDelegate,
const TfTokenVector& requiredPrimvars,
HdRprim& refThis,
UpdatePrimvarInfoFunc& updatePrimvarInfo)
{
#if !defined(HD_API_VERSION) || HD_API_VERSION < 49
using HdStExtCompCpuComputation = HdExtCompCpuComputation;
using HdStExtCompCpuComputationSharedPtr = HdExtCompCpuComputationSharedPtr;
#endif

// Recomputed from scratch on every call, so that a prim which stops being skinned
// (its binding removed, say) goes back to reading the authored points.
_pointsFromSkel = false;

const SdfPath& id = refThis.GetId();

// The compPrimvars are a description of the link between the compute system and
// what we need to draw.
HdExtComputationPrimvarDescriptorVector compPrimvars
= sceneDelegate->GetExtComputationPrimvarDescriptors(id, HdInterpolationVertex);
if (compPrimvars.empty()) {
return;
}

const HdRenderIndex& renderIndex = sceneDelegate->GetRenderIndex();

for (const auto& primvarName : requiredPrimvars) {
auto result
= std::find_if(compPrimvars.begin(), compPrimvars.end(), [&](const auto& compPrimvar) {
return compPrimvar.name == primvarName;
});
// if there is no compute for the given required primvar then we're done!
if (result == compPrimvars.end())
continue;
HdExtComputationPrimvarDescriptor compPrimvar = *result;

// The compPrimvar has the Id of the compute the data comes from, and the output
// of the compute which contains the data.
HdExtComputation const* sourceComp
= static_cast<HdExtComputation const*>(renderIndex.GetSprim(
HdPrimTypeTokens->extComputation, compPrimvar.sourceComputationId));
if (!sourceComp || sourceComp->GetElementCount() <= 0)
continue;

// Create the HdStExtCompCpuComputation objects necessary to resolve the computation.
HdStExtCompCpuComputationSharedPtr cpuComputation;
HdBufferSourceSharedPtrVector sources;
// There is a possible data race calling CreateComputation, see
// https://github.com/PixarAnimationStudios/USD/issues/1742
cpuComputation
= HdStExtCompCpuComputation::CreateComputation(sceneDelegate, *sourceComp, &sources);

// Immediately resolve the computation so we can fill in the primvar info.
for (HdBufferSourceSharedPtr& source : sources) {
source->Resolve();
}

// Pull the result out of the compute and save it into the local primvar info.
size_t outputIndex
= cpuComputation->GetOutputIndex(compPrimvar.sourceComputationOutputName);
// INVALID_OUTPUT_INDEX is declared static in USD, can't access here so re-declare
constexpr size_t INVALID_OUTPUT_INDEX = std::numeric_limits<size_t>::max();
if (INVALID_OUTPUT_INDEX != outputIndex) {
updatePrimvarInfo(
primvarName, cpuComputation->GetOutputByIndex(outputIndex), HdInterpolationVertex);
}

// Records that points primvar is computed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we not record it as computed if we got INVALID_OUTPUT_INDEX above?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good point. This was copied as it is from mesh.cpp, but I think it's worth fixing in this case.

if (primvarName == HdTokens->points) {
_pointsFromSkel = true;
}
}
}

#ifdef MAYA_HAS_DISPLAY_LAYER_API
void MayaUsdRPrim::_ProcessDisplayLayerModes(
const MObject& displayLayerObj,
Expand Down Expand Up @@ -838,6 +929,64 @@
}
}

void MayaUsdRPrim::_SyncEndCommon(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The code in these functions here were also extracted from places that already existed. They were made into a function so that it can be reused into the other shapes.

HdRprim& refThis,
HdSceneDelegate* delegate,
HdDirtyBits* dirtyBits,
TfToken const& reprToken,
HdRprimSharedData& sharedData,
ReprVector const& reprs)
{
#if PXR_VERSION > 2111
const TfToken& renderTag = refThis.GetRenderTag();
#else
const TfToken& renderTag = delegate->GetRenderTag(refThis.GetId());
#endif

_SyncSharedData(sharedData, delegate, dirtyBits, reprToken, refThis, reprs, renderTag);

*dirtyBits = HdChangeTracker::Clean;
}

void MayaUsdRPrim::_RequirePrimvar(TfTokenVector& requiredPrimvars, const TfToken& primvar)
{
if (std::find(requiredPrimvars.cbegin(), requiredPrimvars.cend(), primvar)
== requiredPrimvars.cend()) {
requiredPrimvars.push_back(primvar);
}
}

void MayaUsdRPrim::_CommitPositionsBuffer(
MHWRender::MVertexBuffer* positionsBuffer,
const VtVec3fArray& points) const
{
if (!positionsBuffer) {
return;
}

const size_t numVertices = points.size();

void* bufferData = positionsBuffer->acquire(numVertices, true);
if (!bufferData) {
return;
}

memcpy(bufferData, points.cdata(), sizeof(GfVec3f) * numVertices);

Check notice on line 974 in lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.cpp

View check run for this annotation

Autodesk Chorus / security/flawfinder

memcpy

Does not check for buffer overflows when copying to destination (CWE-120).

// Capture class member for lambda
const MString& rprimId = _rprimId;

_delegate->GetVP2ResourceRegistry().EnqueueCommit([positionsBuffer, bufferData, rprimId]() {
MProfilingScope profilingScope(
HdVP2RenderDelegate::sProfilerCategory,
MProfiler::kColorC_L2,
rprimId.asChar(),
"CommitPositions");

positionsBuffer->commit(bufferData);
});
}

void MayaUsdRPrim::_SyncSharedData(
HdRprimSharedData& sharedData,
HdSceneDelegate* delegate,
Expand Down
53 changes: 53 additions & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/mayaPrimCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,42 @@ class MayaUsdRPrim
ReprVector const& reprs,
TfToken const& renderTag);

//! Shared tail of an rprim's Sync(): resolve the render tag, sync the shared data and
//! clear the dirty bits.
void _SyncEndCommon(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

_SyncEndCommon, _RequirePrimvar, _ResolvePoints, _CommitPositionsBuffer
Are just helper functions that has code being repeated in the different shapes (mesh, points and curves). Moved them to a function to make it easier to reuse them.

HdRprim& refThis,
HdSceneDelegate* delegate,
HdDirtyBits* dirtyBits,
TfToken const& reprToken,
HdRprimSharedData& sharedData,
ReprVector const& reprs);

//! Append \p primvar to \p requiredPrimvars unless it is already there.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not sure what the \p do here and in other comments

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Doxygen provides the command \p for indicating that the next word is a parameter to the function.

static void _RequirePrimvar(TfTokenVector& requiredPrimvars, const TfToken& primvar);

//! Return the points to render. These normally come straight from the scene delegate,
//! but for a UsdSkel-skinned prim the delegate reports the undeformed rest points and
//! the deformed ones are the output of an HdExtComputation, which
template <typename PrimvarSourceMap>
VtValue _ResolvePoints(
HdSceneDelegate* delegate,
const SdfPath& id,
const PrimvarSourceMap& primvarSourceMap) const
{
if (_pointsFromSkel) {
const auto it = primvarSourceMap.find(HdTokens->points);
if (it != primvarSourceMap.end()) {
return it->second.data;
}
}
return delegate->Get(id, HdTokens->points);
}

//! Copy \p points into \p positionsBuffer and enqueue the commit of that buffer.
void _CommitPositionsBuffer(
MHWRender::MVertexBuffer* positionsBuffer,
const VtVec3fArray& points) const;

void _SyncDisplayLayerModes(SdfPath const& id, bool instancedPrim);
void _SyncDisplayLayerModesInstanced(SdfPath const& id, unsigned int instanceCount);

Expand Down Expand Up @@ -329,6 +365,19 @@ class MayaUsdRPrim
UpdatePrimvarInfoFunc& updatePrimvarInfo,
ErasePrimvarInfoFunc& erasePrimvarInfo);

//! Resolve any HdExtComputation which supplies one of \p requiredPrimvars, and feed the
//! computed value back through \p updatePrimvarInfo, replacing the authored primvar.
//! This is how UsdSkel delivers deformed points to the render delegate: UsdImaging
//! publishes the rest points as the authored primvar and the skinned points as the
//! output of a computation.
//! Also refreshes _pointsFromSkel, so every rprim that calls this automatically gets
//! the transform-dirtying behaviour _PropagateDirtyBitsCommon() applies.
void _UpdateComputedPrimvarSourcesGeneric(
HdSceneDelegate* sceneDelegate,
const TfTokenVector& requiredPrimvars,
HdRprim& refThis,
UpdatePrimvarInfoFunc& updatePrimvarInfo);

SdfPath _GetUpdatedMaterialId(HdRprim* rprim, HdSceneDelegate* delegate);
MColor _GetHighlightColor(const TfToken& className);
MColor _GetHighlightColor(const TfToken& className, HdVP2SelectionStatus selectionStatus);
Expand Down Expand Up @@ -388,6 +437,10 @@ class MayaUsdRPrim
//! Selection status of the Rprim
HdVP2SelectionStatus _selectionStatus { kUnselected };

//! Record if the points positions are generated by a UsdSkel, i.e. come from an
//! HdExtComputation rather than from the authored points primvar.
bool _pointsFromSkel { false };

//! Modes requested by display layer along with the frame they are updated on
bool _useInstancedDisplayLayerModes { false };
DisplayLayerModes _displayLayerModes;
Expand Down
Loading
Loading