From aba1ae86916734690b7a1b00db43da195e0a00a8 Mon Sep 17 00:00:00 2001 From: Huidong Chen Date: Tue, 12 Nov 2019 11:15:36 -0500 Subject: [PATCH 1/5] MAYA-100929 wireframe display for USD --- lib/render/vp2RenderDelegate/draw_item.cpp | 6 +- lib/render/vp2RenderDelegate/draw_item.h | 89 +- lib/render/vp2RenderDelegate/mesh.cpp | 1051 ++++++++++------- lib/render/vp2RenderDelegate/mesh.h | 80 +- .../vp2RenderDelegate/proxyRenderDelegate.cpp | 199 ++-- .../vp2RenderDelegate/proxyRenderDelegate.h | 25 +- .../vp2RenderDelegate/render_delegate.cpp | 124 +- .../vp2RenderDelegate/render_delegate.h | 4 +- 8 files changed, 958 insertions(+), 620 deletions(-) diff --git a/lib/render/vp2RenderDelegate/draw_item.cpp b/lib/render/vp2RenderDelegate/draw_item.cpp index 11cf334952..4f6f683d23 100644 --- a/lib/render/vp2RenderDelegate/draw_item.cpp +++ b/lib/render/vp2RenderDelegate/draw_item.cpp @@ -44,13 +44,13 @@ HdVP2DrawItem::HdVP2DrawItem( _renderItemName = GetRprimID().GetText(); _renderItemName += TfStringPrintf("/DrawItem_%p", this).c_str(); - _mesh._indexBuffer.reset( + _renderItemData._indexBuffer.reset( new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32)); if (desc.geomStyle == HdMeshGeomStyleHull) { const MHWRender::MVertexBufferDescriptor desc("", MHWRender::MGeometry::kNormal, MHWRender::MGeometry::kFloat, 3); - _mesh._normalsBuffer.reset(new MHWRender::MVertexBuffer(desc)); + _renderItemData._normalsBuffer.reset(new MHWRender::MVertexBuffer(desc)); } } @@ -67,7 +67,7 @@ HdVP2DrawItem::~HdVP2DrawItem() { //! \brief Get access to render item data. HdVP2DrawItem::RenderItemData& HdVP2DrawItem::GetRenderItemData() { - return _mesh; + return _renderItemData; } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/draw_item.h b/lib/render/vp2RenderDelegate/draw_item.h index c58ec57858..a9abfc40d6 100644 --- a/lib/render/vp2RenderDelegate/draw_item.h +++ b/lib/render/vp2RenderDelegate/draw_item.h @@ -20,16 +20,17 @@ #include "pxr/pxr.h" #include "pxr/base/gf/vec3f.h" #include "pxr/base/vt/array.h" -#include "pxr/imaging/hd/changeTracker.h" #include "pxr/imaging/hd/drawItem.h" +#include "pxr/imaging/hd/mesh.h" #include "pxr/usd/usd/timeCode.h" +#include #include +#include #include PXR_NAMESPACE_OPEN_SCOPE -struct HdMeshReprDesc; class HdVP2RenderDelegate; /*! \brief Draw Item holds information necessary for accessing and updating VP2 render items @@ -38,12 +39,6 @@ class HdVP2RenderDelegate; class HdVP2DrawItem final : public HdDrawItem { public: - //! Helper struct providing storage for per frame cache data - struct CachedData { - MBoundingBox _boundingBox; //!< Bounding box cache - VtArray _normals; //!< Normals cache - }; - //! A primvar vertex buffer map indexed by primvar name. using PrimvarBufferMap = std::unordered_map< TfToken, @@ -61,12 +56,30 @@ class HdVP2DrawItem final : public HdDrawItem PrimvarBufferMap _primvarBuffers; //! Render item index buffer - use when updating data std::unique_ptr _indexBuffer; + //! Bounding box of the render item. + MBoundingBox _boundingBox; + //! World matrix of the render item. + MMatrix _worldMatrix; + + //! Shader instance assigned to the render item. No ownership is held. + MHWRender::MShaderInstance* _shader{ nullptr }; + + //! Whether or not the render item is enabled + bool _enabled{ true }; //! Number of instances currently allocated for render item unsigned int _instanceCount{ 0 }; - //! Per frame cache - std::map _cache; + //! Whether or not the render item is using GPU instanced draw. + bool _usingInstancedDraw{ false }; + }; + + //! Bit fields indicating what the render item is created for. A render item + //! can be created for multiple usages. + enum RenderItemUsage + { + kRegular = 1 << 0, //!< Regular drawing (shaded, wireframe etc.) + kSelectionHighlight = 1 << 1 //!< Selection highlight. }; public: @@ -80,24 +93,62 @@ class HdVP2DrawItem final : public HdDrawItem */ const MString& GetRenderItemName() const { return _renderItemName; } - /*! \brief Whether the draw item is enabled. + /*! \brief Get pointer of the associated render item */ - bool IsEnabled() const { return _enabled; } + MHWRender::MRenderItem* GetRenderItem() const { return _renderItem; } - /*! \brief Enable or disable the draw item. + /*! \brief Set pointer of the associated render item */ - void Enable(bool v) { _enabled = v; } + void SetRenderItem(MHWRender::MRenderItem* item) { _renderItem = item; } /*! \brief Get the repr desc for which the draw item was created. */ const HdMeshReprDesc& GetReprDesc() const { return _reprDesc; } + /*! \brief Set a usage to the render item + */ + void SetUsage(RenderItemUsage usage) { _renderItemUsage = usage; } + + /*! \brief Add a usage to the render item + */ + void AddUsage(RenderItemUsage usage) { _renderItemUsage |= usage; } + + /*! \brief Is the render item created for this usage? + */ + bool ContainsUsage(RenderItemUsage usage) const { return (_renderItemUsage & usage) != 0; } + + /*! \brief Is the render item created for this usage only? + */ + bool MatchesUsage(RenderItemUsage usage) const { return _renderItemUsage == usage; } + + /*! \brief Bitwise OR with the input dirty bits. + */ + void SetDirtyBits(HdDirtyBits bits) { + _dirtyBits |= bits; + } + + /*! \brief Reset the dirty bits to clean. + */ + void ResetDirtyBits() { + _dirtyBits = 0; + } + + /*! \brief Get the dirty bits of the draw items. + */ + HdDirtyBits GetDirtyBits() const { + return _dirtyBits; + } + private: - HdVP2RenderDelegate* _delegate{ nullptr }; //!< VP2 render delegate for which this draw item was created - const HdMeshReprDesc _reprDesc; //!< The repr desc for which the draw item was created. - RenderItemData _mesh; //!< VP2 render item data - MString _renderItemName; //!< Unique name. Use this when searching for render item in subscene override container - bool _enabled{ true }; //!< Whether the draw item is enabled. + + HdVP2RenderDelegate* _delegate{ nullptr }; //!< VP2 render delegate for which this draw item was created + const HdMeshReprDesc _reprDesc; //!< The repr desc for which the draw item was created. + MString _renderItemName; //!< Unique name for easier debugging and profiling. + MHWRender::MRenderItem* _renderItem{ nullptr }; //!< Pointer of the render item for fast access. No ownership is held. + RenderItemData _renderItemData; //!< VP2 render item data + + uint32_t _renderItemUsage{ kRegular }; //!< What is the render item created for + HdDirtyBits _dirtyBits{ HdChangeTracker::AllDirty }; //!< Dirty bits to control data update of render item }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/mesh.cpp b/lib/render/vp2RenderDelegate/mesh.cpp index b9f7e29f25..629745d53a 100644 --- a/lib/render/vp2RenderDelegate/mesh.cpp +++ b/lib/render/vp2RenderDelegate/mesh.cpp @@ -20,13 +20,14 @@ #include "material.h" #include "instancer.h" #include "proxyRenderDelegate.h" +#include "render_delegate.h" #include "pxr/base/gf/matrix4d.h" #include "pxr/imaging/hd/sceneDelegate.h" #include "pxr/imaging/hd/meshUtil.h" #include "pxr/imaging/hd/smoothNormals.h" #include "pxr/imaging/hd/vertexAdjacency.h" -#include "pxr/imaging/pxOsd/tokens.h" +#include "pxr/imaging/hdx/tokens.h" #include #include @@ -38,10 +39,27 @@ PXR_NAMESPACE_OPEN_SCOPE namespace { + //! Required primvars when there is no material binding. + const TfTokenVector sFallbackShaderPrimvars = { + HdTokens->displayColor, + HdTokens->displayOpacity, + HdTokens->normals + }; + + const MColor kSelectionHighlightColor(0.056f, 1.0f, 0.366f, 1.0f); //!< Selection highlight color + const MColor kOpaqueBlue(0.0f, 0.0f, 1.0f, 1.0f); //!< Opaque blue + const MColor kOpaqueGray(.18f, .18f, .18f, 1.0f); //!< Opaque gray + const unsigned int kNumColorChannels = 4; //!< The number of color channels + + const MString kPositionsStr("positions"); //!< Cached string for efficiency + const MString kNormalsStr("normals"); //!< Cached string for efficiency + const MString kDiffuseColorStr("diffuseColor"); //!< Cached string for efficiency + const MString kSolidColorStr("solidColor"); //!< Cached string for efficiency + //! A primvar vertex buffer data map indexed by primvar name. using PrimvarBufferDataMap = std::unordered_map< TfToken, - float*, + void*, TfToken::HashFunctor >; @@ -50,33 +68,39 @@ namespace { struct CommitState { HdVP2DrawItem::RenderItemData& _drawItemData; - //! If valid, new position buffer data to commit - float* _positionBufferData{ nullptr }; //! If valid, new index buffer data to commit int* _indexBufferData{ nullptr }; //! If valid, new color buffer data to commit - float* _colorBufferData{ nullptr }; + void* _colorBufferData{ nullptr }; //! If valid, new normals buffer data to commit - float* _normalsBufferData{ nullptr }; + void* _normalsBufferData{ nullptr }; //! If valid, new primvar buffer data to commit PrimvarBufferDataMap _primvarBufferDataMap; - //! If valid, new shader instance to set - MHWRender::MShaderInstance* _surfaceShader{ nullptr }; + //! If valid, world matrix to set on the render item + MMatrix* _worldMatrix{ nullptr }; - //! Instancing doesn't have dirty bits, every time we do update, we must update instance transforms - MMatrixArray _instanceTransforms; + //! If valid, bounding box to set on the render item + MBoundingBox* _boundingBox{ nullptr }; + + //! if valid, enable or disable the render item + bool* _enabled{ nullptr }; + + //! If valid, new shader instance to set + MHWRender::MShaderInstance* _shader{ nullptr }; //! Is this object transparent bool _isTransparent{ false }; - //! Capture of what has changed on this rprim - HdDirtyBits _dirtyBits; + //! Instancing doesn't have dirty bits, every time we do update, we must update instance transforms + MMatrixArray _instanceTransforms; + + //! Color array to support per-instance color and selection highlight. + MFloatArray _instanceColors; //! Construct valid commit state - CommitState(HdVP2DrawItem& drawItem, HdDirtyBits& dirtyBits) - : _drawItemData(drawItem.GetRenderItemData()) - , _dirtyBits(dirtyBits) {} + CommitState(HdVP2DrawItem& item) : _drawItemData(item.GetRenderItemData()) + {} //! No default constructor, we need draw item and dirty bits. CommitState() = delete; @@ -107,7 +131,7 @@ namespace { size_t numVertices, size_t channelOffset, bool requiresUnsharedVertices, - const SdfPath& meshId, + const MString& rprimId, const HdMeshTopology& topology, const TfToken& primvarName, const VtArray& primvarData, @@ -138,7 +162,7 @@ namespace { TF_CODING_ERROR("Invalid Hydra prim '%s': primvar %s " "requires %zu unshared elements, while the number of " "face vertices is %zu. Skipping primvar update.", - meshId.GetText(), primvarName.GetText(), + rprimId.asChar(), primvarName.GetText(), numVertices, faceVertexIndices.size()); } } @@ -149,7 +173,7 @@ namespace { TF_DEBUG(HDVP2_DEBUG_MESH).Msg("Invalid Hydra prim '%s': " "primvar %s has %zu elements, while its topology " "references only upto element index %zu.\n", - meshId.GetText(), primvarName.GetText(), + rprimId.asChar(), primvarName.GetText(), primvarData.size(), numVertices); } @@ -170,7 +194,7 @@ namespace { TF_DEBUG(HDVP2_DEBUG_MESH).Msg("Invalid Hydra prim '%s': " "primvar %s has only %zu elements, while its topology expects " "at least %zu elements. Skipping primvar update.\n", - meshId.GetText(), primvarName.GetText(), + rprimId.asChar(), primvarName.GetText(), primvarData.size(), numVertices); memset(vertexBuffer, 0, sizeof(DEST_TYPE) * numVertices); @@ -187,7 +211,7 @@ namespace { TF_DEBUG(HDVP2_DEBUG_MESH).Msg("Invalid Hydra prim '%s': " "primvar %s has %zu elements, while its topology " "references only upto element index %zu.\n", - meshId.GetText(), primvarName.GetText(), + rprimId.asChar(), primvarName.GetText(), primvarData.size(), numFaces); } @@ -207,7 +231,7 @@ namespace { TF_DEBUG(HDVP2_DEBUG_MESH).Msg("Invalid Hydra prim '%s': " "primvar %s has only %zu elements, while its topology expects " "at least %zu elements. Skipping primvar update.\n", - meshId.GetText(), primvarName.GetText(), + rprimId.asChar(), primvarName.GetText(), primvarData.size(), numFaces); memset(vertexBuffer, 0, sizeof(DEST_TYPE) * numVertices); @@ -216,7 +240,7 @@ namespace { else { TF_CODING_ERROR("Invalid Hydra prim '%s': " "vertex unsharing is required for uniform primvar %s", - meshId.GetText(), primvarName.GetText()); + rprimId.asChar(), primvarName.GetText()); } break; case HdInterpolationFaceVarying: @@ -229,7 +253,7 @@ namespace { TF_DEBUG(HDVP2_DEBUG_MESH).Msg("Invalid Hydra prim '%s': " "primvar %s has %zu elements, while its topology references " "only upto element index %zu.\n", - meshId.GetText(), primvarName.GetText(), + rprimId.asChar(), primvarName.GetText(), primvarData.size(), numVertices); } @@ -250,7 +274,7 @@ namespace { TF_DEBUG(HDVP2_DEBUG_MESH).Msg("Invalid Hydra prim '%s': " "primvar %s has only %zu elements, while its topology expects " "at least %zu elements. Skipping primvar update.\n", - meshId.GetText(), primvarName.GetText(), + rprimId.asChar(), primvarName.GetText(), primvarData.size(), numVertices); memset(vertexBuffer, 0, sizeof(DEST_TYPE) * numVertices); @@ -259,13 +283,13 @@ namespace { else { TF_CODING_ERROR("Invalid Hydra prim '%s': " "vertex unsharing is required face-varying primvar %s", - meshId.GetText(), primvarName.GetText()); + rprimId.asChar(), primvarName.GetText()); } break; default: TF_CODING_ERROR("Invalid Hydra prim '%s': " "unimplemented interpolation %d for primvar %s", - meshId.GetText(), (int)primvarInterp, primvarName.GetText()); + rprimId.asChar(), (int)primvarInterp, primvarName.GetText()); break; } } @@ -319,11 +343,13 @@ namespace { //! \brief Constructor HdVP2Mesh::HdVP2Mesh(HdVP2RenderDelegate* delegate, const SdfPath& id, const SdfPath& instancerId) - : HdMesh(id, instancerId) - , _delegate(delegate) { +: HdMesh(id, instancerId) +, _delegate(delegate) +, _rprimId(id.GetText()) +{ const MHWRender::MVertexBufferDescriptor vbDesc( "", MHWRender::MGeometry::kPosition, MHWRender::MGeometry::kFloat, 3); - _positionsBuffer.reset(new MHWRender::MVertexBuffer(vbDesc)); + _meshSharedData._positionsBuffer.reset(new MHWRender::MVertexBuffer(vbDesc)); } //! \brief Destructor @@ -332,37 +358,145 @@ HdVP2Mesh::~HdVP2Mesh() { //! \brief Synchronize VP2 state with scene delegate state based on dirty bits and representation void HdVP2Mesh::Sync( - HdSceneDelegate* delegate, HdRenderParam* renderParam, - HdDirtyBits* dirtyBits, const TfToken& reprToken) { - + HdSceneDelegate* delegate, + HdRenderParam* renderParam, + HdDirtyBits* dirtyBits, + const TfToken& reprToken) +{ MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, - MProfiler::kColorC_L2, GetId().GetText(), "HdVP2Mesh Sync"); + MProfiler::kColorC_L2, _rprimId.asChar(), "HdVP2Mesh::Sync"); + + const SdfPath& id = GetId(); if (*dirtyBits & HdChangeTracker::DirtyMaterialId) { _SetMaterialId(delegate->GetRenderIndex().GetChangeTracker(), - delegate->GetMaterialId(GetId())); + delegate->GetMaterialId(id)); } - _UpdateRepr(delegate, reprToken, dirtyBits); + if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->normals) || + HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->primvar)) { + const HdVP2Material* material = static_cast( + delegate->GetRenderIndex().GetSprim( + HdPrimTypeTokens->material, GetMaterialId()) + ); - auto* const param = static_cast(_delegate->GetRenderParam()); - // HdC_TODO: Currently we are running selection highlighting update in a separate execution - // and this execution will update only wire representation. The next execution will update - // remaining representations, but if we clear dirty bits, nothing will get updated. - // We leave the dirty bits unmodified during selection highlighting to workaround the issue, - // but this is not ideal - we shouldn't have to evaluate the same data twice. - if (!param->GetDrawScene().InSelectionHighlightUpdate()) - *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits; + const TfTokenVector& requiredPrimvars = + material && material->GetSurfaceShader() ? + material->GetRequiredPrimvars() : sFallbackShaderPrimvars; + + _UpdatePrimvarSources(delegate, *dirtyBits, requiredPrimvars); + } + + if (HdChangeTracker::IsTopologyDirty(*dirtyBits, id)) { + _meshSharedData._topology = GetMeshTopology(delegate); + + // If there is uniform or face-varying primvar, we have to expand shared + // vertices in CPU because OpenGL SSBO technique is not widely supported + // on GPUs and 3D APIs. + bool requiresUnsharedVertices = false; + for (const auto& it : _meshSharedData._primvarSourceMap) { + const HdInterpolation interp = it.second.interpolation; + if (interp == HdInterpolationUniform || + interp == HdInterpolationFaceVarying) { + requiresUnsharedVertices = true; + break; + } + } + + if (requiresUnsharedVertices) { + const HdMeshTopology& topology = _meshSharedData._topology; + + // Fill with sequentially increasing values, starting from 0. The + // new face vertex indices will then be implicitly used to assemble + // all primvar vertex buffers. + VtIntArray newFaceVtxIds; + newFaceVtxIds.resize(topology.GetFaceVertexIndices().size()); + std::iota(newFaceVtxIds.begin(), newFaceVtxIds.end(), 0); + + _meshSharedData._unsharedTopology.reset( + new HdMeshTopology( + topology.GetScheme(), + topology.GetOrientation(), + topology.GetFaceVertexCounts(), + newFaceVtxIds, + topology.GetHoleIndices(), + topology.GetRefineLevel() + ) + ); + } + else { + _meshSharedData._unsharedTopology.reset(nullptr); + } + } + + // 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); + _meshSharedData._points = value.Get(); + + const HdMeshTopology& topology = _meshSharedData._topology; + + const bool requiresUnsharedVertices = + _meshSharedData._unsharedTopology != nullptr; + + const size_t numVertices = requiresUnsharedVertices ? + topology.GetFaceVertexIndices().size() : + topology.GetNumPoints(); + + void* bufferData = _meshSharedData._positionsBuffer->acquire(numVertices, true); + if (bufferData) { + _FillPrimvarData(static_cast(bufferData), + numVertices, 0, requiresUnsharedVertices, + _rprimId, topology, + HdTokens->points, _meshSharedData._points, HdInterpolationVertex); + + // Capture class member for lambda + MHWRender::MVertexBuffer* const positionsBuffer = + _meshSharedData._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); + } + ); + } + } + + if (HdChangeTracker::IsExtentDirty(*dirtyBits, id)) { + _sharedData.bounds.SetRange(delegate->GetExtent(id)); + } + + if (HdChangeTracker::IsTransformDirty(*dirtyBits, id)) { + _sharedData.bounds.SetMatrix(delegate->GetTransform(id)); + } + + if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) { + _sharedData.visible = delegate->GetVisible(id); + } + + *dirtyBits = HdChangeTracker::Clean; + + // Draw item update is controlled by its own dirty bits. + _UpdateRepr(delegate, reprToken); } /*! \brief Returns the minimal set of dirty bits to place in the change tracker for use in the first sync of this prim. */ HdDirtyBits HdVP2Mesh::GetInitialDirtyBitsMask() const { - return HdChangeTracker::Clean | HdChangeTracker::InitRepr | - HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyTopology | - HdChangeTracker::DirtyTransform | HdChangeTracker::DirtyMaterialId | - HdChangeTracker::DirtyPrimvar | HdChangeTracker::DirtyVisibility | HdChangeTracker::DirtyInstanceIndex; + return HdChangeTracker::InitRepr | + HdChangeTracker::DirtyPoints | + HdChangeTracker::DirtyTopology | + HdChangeTracker::DirtyTransform | + HdChangeTracker::DirtyMaterialId | + HdChangeTracker::DirtyPrimvar | + HdChangeTracker::DirtyVisibility | + HdChangeTracker::DirtyInstanceIndex; } /*! \brief Add additional dirty bits @@ -435,6 +569,17 @@ HdDirtyBits HdVP2Mesh::_PropagateDirtyBits(HdDirtyBits bits) const { bits |= HdChangeTracker::DirtyExtent; } + // Propagate dirty bits to all draw items. + for (const std::pair& pair : _reprs) { + const HdReprSharedPtr& repr = pair.second; + const HdRepr::DrawItems& items = repr->GetDrawItems(); + for (HdDrawItem* item : items) { + if (HdVP2DrawItem* drawItem = static_cast(item)) { + drawItem->SetDirtyBits(bits); + } + } + } + return bits; } @@ -456,39 +601,48 @@ HdDirtyBits HdVP2Mesh::_PropagateDirtyBits(HdDirtyBits bits) const { See HdRprim::InitRepr() */ void HdVP2Mesh::_InitRepr(const TfToken& reprToken, HdDirtyBits* dirtyBits) { - _ReprVector::iterator it = std::find_if(_reprs.begin(), _reprs.end(), _ReprComparator(reprToken)); - - HdReprSharedPtr repr; - - const bool isNew = (it == _reprs.end()); - if (isNew) { - _reprs.emplace_back(reprToken, boost::make_shared()); - repr = _reprs.back().second; - } - else { - repr = it->second; - } - auto* const param = static_cast(_delegate->GetRenderParam()); MSubSceneContainer* subSceneContainer = param->GetContainer(); - if (!subSceneContainer) + if (ARCH_UNLIKELY(!subSceneContainer)) return; - // Selection highlight uses the wire repr for now, hoping it will be able - // to share draw items with future implementation of wireframe mode. If - // it won't, we can then define a customized "selectionHighlight" repr. - if (reprToken == HdReprTokens->wire) { - const bool selected = param->GetDrawScene().IsProxySelected() || - (param->GetDrawScene().GetPrimSelectionState(GetId()) != nullptr); - if (_EnableWireDrawItems(repr, dirtyBits, selected)) - return; + // We don't create a repr for the selection token because it serves for + // selection state update only. Mark DirtySelection bit that will be + // automatically propagated to all draw items of the rprim. + if (reprToken == HdxTokens->selection) { + const HdVP2SelectionStatus selectionState = + param->GetDrawScene().GetPrimSelectionStatus(GetId()); + if (_selectionState != selectionState) { + _selectionState = selectionState; + *dirtyBits |= DirtySelection; + } + else if (_selectionState == kPartiallySelected) { + *dirtyBits |= DirtySelection; + } + return; } - else if (!isNew) { + + // If the repr has any draw item with the DirtySelection bit, mark the + // DirtySelectionHighlight bit to invoke the synchronization call. + _ReprVector::iterator it = std::find_if( + _reprs.begin(), _reprs.end(), _ReprComparator(reprToken)); + if (it != _reprs.end()) { + const HdReprSharedPtr& repr = it->second; + const HdRepr::DrawItems& items = repr->GetDrawItems(); + for (const HdDrawItem* item : items) { + const HdVP2DrawItem* drawItem = static_cast(item); + if (drawItem && (drawItem->GetDirtyBits() & DirtySelection)) { + *dirtyBits |= DirtySelectionHighlight; + break; + } + } return; } - // set dirty bit to say we need to sync a new repr (buffer array - // ranges may change) + _reprs.emplace_back(reprToken, boost::make_shared()); + HdReprSharedPtr repr = _reprs.back().second; + + // set dirty bit to say we need to sync a new repr *dirtyBits |= HdChangeTracker::NewRepr; _MeshReprConfig::DescArray descs = _GetReprDesc(reprToken); @@ -505,14 +659,45 @@ void HdVP2Mesh::_InitRepr(const TfToken& reprToken, HdDirtyBits* dirtyBits) { const MString& renderItemName = drawItem->GetRenderItemName(); - MHWRender::MRenderItem* const renderItem = - _CreateRenderItem(renderItemName, desc); + MHWRender::MRenderItem* renderItem = nullptr; - _delegate->GetVP2ResourceRegistry().EnqueueCommit( - [subSceneContainer, renderItem]() { - subSceneContainer->add(renderItem); + switch (desc.geomStyle) { + case HdMeshGeomStyleHull: + renderItem = _CreateSmoothHullRenderItem(renderItemName); + break; + case HdMeshGeomStyleHullEdgeOnly: + // The smoothHull repr uses the wireframe item for selection + // highlight only. + if (reprToken == HdReprTokens->smoothHull) { + renderItem = _CreateSelectionHighlightRenderItem(renderItemName); + drawItem->SetUsage(HdVP2DrawItem::kSelectionHighlight); } - ); + // The wireframe item is used for both regular drawing and + // selection highlight. + else { + renderItem = _CreateWireframeRenderItem(renderItemName); + drawItem->AddUsage(HdVP2DrawItem::kSelectionHighlight); + } + break; + case HdMeshGeomStylePoints: + renderItem = _CreatePointsRenderItem(renderItemName); + break; + default: + TF_WARN("Unsupported geomStyle"); + break; + } + + if (renderItem) { + // Store the render item pointer to avoid expensive lookup in the + // subscene container. + drawItem->SetRenderItem(renderItem); + + _delegate->GetVP2ResourceRegistry().EnqueueCommit( + [subSceneContainer, renderItem]() { + subSceneContainer->add(renderItem); + } + ); + } } if (desc.geomStyle == HdMeshGeomStyleHull) { @@ -529,9 +714,6 @@ void HdVP2Mesh::_InitRepr(const TfToken& reprToken, HdDirtyBits* dirtyBits) { } } } - else if (desc.geomStyle == HdMeshGeomStyleHullEdgeOnly) { - *dirtyBits |= HdChangeTracker::DirtyTopology; - } } } @@ -540,7 +722,13 @@ void HdVP2Mesh::_InitRepr(const TfToken& reprToken, HdDirtyBits* dirtyBits) { Repr objects are created to support specific reprName tokens, and contain a list of HdVP2DrawItems and corresponding RenderItems. */ -void HdVP2Mesh::_UpdateRepr(HdSceneDelegate *sceneDelegate, const TfToken& reprToken, HdDirtyBits *dirtyBits) { +void HdVP2Mesh::_UpdateRepr(HdSceneDelegate *sceneDelegate, const TfToken& reprToken) +{ + // We don't have a repr for the selection token because it serves as a tag + // for selection update only. + if (reprToken == HdxTokens->selection) { + return; + } HdReprSharedPtr const &curRepr = _GetRepr(reprToken); if (!curRepr) { @@ -555,7 +743,7 @@ void HdVP2Mesh::_UpdateRepr(HdSceneDelegate *sceneDelegate, const TfToken& reprT bool requireSmoothNormals = false; bool requireFlatNormals = false; for (size_t descIdx = 0; descIdx < reprDescs.size(); ++descIdx) { - const HdMeshReprDesc &desc = reprDescs[descIdx]; + const HdMeshReprDesc& desc = reprDescs[descIdx]; if (desc.geomStyle == HdMeshGeomStyleHull) { if (desc.flatShadingEnabled) { requireFlatNormals = true; @@ -569,12 +757,9 @@ void HdVP2Mesh::_UpdateRepr(HdSceneDelegate *sceneDelegate, const TfToken& reprT // For each relevant draw item, update dirty buffer sources. const HdRepr::DrawItems& items = curRepr->GetDrawItems(); for (HdDrawItem* item : items) { - if (HdChangeTracker::IsDirty(*dirtyBits)) { - if (auto* drawItem = static_cast(item)) { - const HdMeshReprDesc &desc = drawItem->GetReprDesc(); - _UpdateDrawItem(sceneDelegate, drawItem, dirtyBits, desc, - requireSmoothNormals, requireFlatNormals); - } + if (auto* drawItem = static_cast(item)) { + _UpdateDrawItem(sceneDelegate, drawItem, + requireSmoothNormals, requireFlatNormals); } } } @@ -587,103 +772,43 @@ void HdVP2Mesh::_UpdateRepr(HdSceneDelegate *sceneDelegate, const TfToken& reprT void HdVP2Mesh::_UpdateDrawItem( HdSceneDelegate* sceneDelegate, HdVP2DrawItem* drawItem, - HdDirtyBits* dirtyBits, - const HdMeshReprDesc &desc, bool requireSmoothNormals, - bool requireFlatNormals -) { - auto* const param = static_cast(_delegate->GetRenderParam()); - MSubSceneContainer* subSceneContainer = param->GetContainer(); - if (!subSceneContainer) - return; - - CommitState stateToCommit(*drawItem, *dirtyBits); + bool requireFlatNormals) +{ + CommitState stateToCommit(*drawItem); HdVP2DrawItem::RenderItemData& drawItemData = stateToCommit._drawItemData; - - static constexpr bool writeOnly = true; - - const SdfPath& id = GetId(); - - const bool topologyDirty = - HdChangeTracker::IsTopologyDirty(*dirtyBits, id); - const bool pointsDirty = - HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->points); - const bool normalsDirty = - HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->normals); - const bool primvarDirty = - HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->primvar); - - if (topologyDirty) { - _topology = GetMeshTopology(sceneDelegate); - } - - if (pointsDirty) { - const VtValue value = sceneDelegate->Get(id, HdTokens->points); - _points = value.Get(); + const HdDirtyBits itemDirtyBits = drawItem->GetDirtyBits(); + + // We don't need to update the dedicated selection highlight item when there + // is no selection highlight change and the mesh is not selected. Draw item + // has its own dirty bits, so update will be doner when it shows in viewport. + if (drawItem->MatchesUsage(HdVP2DrawItem::kSelectionHighlight) && + ((itemDirtyBits & DirtySelectionHighlight) == 0) && + (_selectionState == kUnselected)) { + return; } - if (normalsDirty || primvarDirty) { - _UpdatePrimvarSources(sceneDelegate, *dirtyBits); - } + const SdfPath& id = GetId(); + const HdMeshReprDesc &desc = drawItem->GetReprDesc(); - TfTokenVector matPrimvars; + auto* const param = static_cast(_delegate->GetRenderParam()); + ProxyRenderDelegate& drawScene = param->GetDrawScene(); const HdRenderIndex& renderIndex = sceneDelegate->GetRenderIndex(); - const HdVP2Material* material = static_cast( - renderIndex.GetSprim(HdPrimTypeTokens->material, GetMaterialId())); - if (material && material->GetSurfaceShader()) { - matPrimvars = material->GetRequiredPrimvars(); - } - else { - matPrimvars.push_back(HdTokens->displayColor); - matPrimvars.push_back(HdTokens->displayOpacity); - matPrimvars.push_back(HdTokens->normals); - } - // If there is uniform or face-varying primvar, we have to expand shared - // vertices in CPU because OpenGL SSBO technique is not widely supported - // on GPUs and 3D APIs. - bool requiresUnsharedVertices = false; - for (const TfToken& pv : matPrimvars) { - const auto it = _primvarSourceMap.find(pv); - if (it != _primvarSourceMap.end()) { - const HdInterpolation interpolation = it->second.interpolation; - if (interpolation == HdInterpolationUniform || - interpolation == HdInterpolationFaceVarying) { - requiresUnsharedVertices = true; - break; - } - } - } + const HdMeshTopology& topology = _meshSharedData._topology; + const HdMeshTopology* unsharedTopology = _meshSharedData._unsharedTopology.get(); + const auto& primvarSourceMap = _meshSharedData._primvarSourceMap; + const bool requiresUnsharedVertices = (unsharedTopology != nullptr); const size_t numVertices = requiresUnsharedVertices ? - _topology.GetFaceVertexIndices().size() : _topology.GetNumPoints(); + topology.GetFaceVertexIndices().size() : topology.GetNumPoints(); // Prepare index buffer. - if (topologyDirty) { - const HdMeshTopology* topologyToUse = &_topology; - HdMeshTopology unsharedTopology; - - if (requiresUnsharedVertices) { - // Fill with sequentially increasing values, starting from 0. The - // new face vertex indices will then be implicitly used to assemble - // all primvar vertex buffers. - VtIntArray newFaceVtxIds; - newFaceVtxIds.resize(_topology.GetFaceVertexIndices().size()); - std::iota(newFaceVtxIds.begin(), newFaceVtxIds.end(), 0); - - unsharedTopology = HdMeshTopology( - _topology.GetScheme(), - _topology.GetOrientation(), - _topology.GetFaceVertexCounts(), - newFaceVtxIds, - _topology.GetHoleIndices(), - _topology.GetRefineLevel() - ); - - topologyToUse = &unsharedTopology; - } + if ((itemDirtyBits & HdChangeTracker::DirtyTopology) != 0) { + const HdMeshTopology* topologyToUse = unsharedTopology ? unsharedTopology : &topology; + // Index data is not required for points repr. if (desc.geomStyle == HdMeshGeomStyleHull) { HdMeshUtil meshUtil(topologyToUse, id); VtVec3iArray trianglesFaceVertexIndices; @@ -693,7 +818,7 @@ void HdVP2Mesh::_UpdateDrawItem( const int numIndex = trianglesFaceVertexIndices.size() * 3; stateToCommit._indexBufferData = static_cast( - drawItemData._indexBuffer->acquire(numIndex, writeOnly)); + drawItemData._indexBuffer->acquire(numIndex, true)); memcpy(stateToCommit._indexBufferData, trianglesFaceVertexIndices.data(), numIndex * sizeof(int)); } @@ -701,26 +826,10 @@ void HdVP2Mesh::_UpdateDrawItem( unsigned int numIndex = _GetNumOfEdgeIndices(*topologyToUse); stateToCommit._indexBufferData = static_cast( - drawItemData._indexBuffer->acquire(numIndex, writeOnly)); + drawItemData._indexBuffer->acquire(numIndex, true)); _FillEdgeIndices(stateToCommit._indexBufferData, *topologyToUse); } - else { - // Index buffer data is not required for point representation. - } - } - - // Prepare position buffer. It is shared among all render items of the rprim - // so it should be updated only once when it gets dirty. - if (pointsDirty) { - void* bufferData = _positionsBuffer->acquire(numVertices, writeOnly); - if (bufferData) { - _FillPrimvarData(static_cast(bufferData), - numVertices, 0, requiresUnsharedVertices, - id, _topology, HdTokens->points, _points, HdInterpolationVertex); - - stateToCommit._positionBufferData = static_cast(bufferData); - } } // Prepare normal buffer. @@ -728,8 +837,8 @@ void HdVP2Mesh::_UpdateDrawItem( VtVec3fArray normals; HdInterpolation interp = HdInterpolationConstant; - const auto it = _primvarSourceMap.find(HdTokens->normals); - if (it != _primvarSourceMap.end()) { + const auto it = primvarSourceMap.find(HdTokens->normals); + if (it != primvarSourceMap.end()) { const VtValue& value = it->second.data; if (ARCH_LIKELY(value.IsHolding())) { normals = value.UncheckedGet(); @@ -743,59 +852,64 @@ void HdVP2Mesh::_UpdateDrawItem( // otherwise, compute smooth normals from points and adjacency and we // have a custom dirty bit to determine whether update is needed. if (!normals.empty()) { - prepareNormals = normalsDirty; + prepareNormals = ((itemDirtyBits & HdChangeTracker::DirtyNormals) != 0); } - else if (requireSmoothNormals && (*dirtyBits & DirtySmoothNormals)) { + else if (requireSmoothNormals && (itemDirtyBits & DirtySmoothNormals)) { // note: normals gets dirty when points are marked as dirty, // at change tracker. - // clear DirtySmoothNormals (this is not a scene dirtybit) - *dirtyBits &= ~DirtySmoothNormals; - - prepareNormals = true; - + // HdC_TODO: move the normals computation to GPU to save expensive + // computation and buffer transfer. Hd_VertexAdjacencySharedPtr adjacency(new Hd_VertexAdjacency()); HdBufferSourceSharedPtr adjacencyComputation = - adjacency->GetSharedAdjacencyBuilderComputation(&_topology); + adjacency->GetSharedAdjacencyBuilderComputation(&topology); adjacencyComputation->Resolve(); // IS the adjacency updated now? // The topology doesn't have to reference all of the points, thus // we compute the number of normals as required by the topology. normals = Hd_SmoothNormals::ComputeSmoothNormals( - adjacency.get(), _topology.GetNumPoints(), _points.cdata()); + adjacency.get(), topology.GetNumPoints(), + _meshSharedData._points.cdata()); interp = HdInterpolationVertex; + + prepareNormals = !normals.empty(); } - if (prepareNormals && !normals.empty()) { - void* bufferData = drawItemData._normalsBuffer->acquire(numVertices, writeOnly); + if (prepareNormals) { + void* bufferData = drawItemData._normalsBuffer->acquire(numVertices, true); if (bufferData) { _FillPrimvarData(static_cast(bufferData), numVertices, 0, requiresUnsharedVertices, - id, _topology, HdTokens->normals, normals, interp); + _rprimId, topology, HdTokens->normals, normals, interp); - stateToCommit._normalsBufferData = static_cast(bufferData); + stateToCommit._normalsBufferData = bufferData; } } } // Prepare color buffer. if (desc.geomStyle == HdMeshGeomStyleHull) { - if (*dirtyBits & HdChangeTracker::DirtyMaterialId) { + if ((itemDirtyBits & HdChangeTracker::DirtyMaterialId) != 0) { + const HdVP2Material* material = static_cast( + renderIndex.GetSprim(HdPrimTypeTokens->material, GetMaterialId()) + ); + if (material) { - stateToCommit._surfaceShader = material->GetSurfaceShader(); - if (stateToCommit._surfaceShader && - stateToCommit._surfaceShader->isTransparent()) { - stateToCommit._isTransparent = true; + MHWRender::MShaderInstance* shader = material->GetSurfaceShader(); + if (shader != nullptr && shader != drawItemData._shader) { + drawItemData._shader = shader; + stateToCommit._shader = shader; + stateToCommit._isTransparent = shader->isTransparent(); } } } - TfTokenVector::const_iterator begin = matPrimvars.cbegin(); - TfTokenVector::const_iterator end = matPrimvars.cend(); + auto itColor = primvarSourceMap.find(HdTokens->displayColor); + auto itOpacity = primvarSourceMap.find(HdTokens->displayOpacity); - if (primvarDirty && - (std::find(begin, end, HdTokens->displayColor) != end || - std::find(begin, end, HdTokens->displayOpacity) != end)) { + if (((itemDirtyBits & HdChangeTracker::DirtyPrimvar) != 0) && + (itColor != primvarSourceMap.end() || + itOpacity != primvarSourceMap.end())) { // If color/opacity is not found, the 18% gray color will be used // to match the default color of Hydra Storm. VtVec3fArray colorArray(1, GfVec3f(0.18f, 0.18f, 0.18f)); @@ -804,21 +918,19 @@ void HdVP2Mesh::_UpdateDrawItem( HdInterpolation colorInterp = HdInterpolationConstant; HdInterpolation alphaInterp = HdInterpolationConstant; - auto it = _primvarSourceMap.find(HdTokens->displayColor); - if (it != _primvarSourceMap.end()) { - const VtValue& value = it->second.data; + if (itColor != primvarSourceMap.end()) { + const VtValue& value = itColor->second.data; if (value.IsHolding() && value.GetArraySize() > 0) { colorArray = value.UncheckedGet(); - colorInterp = it->second.interpolation; + colorInterp = itColor->second.interpolation; } } - it = _primvarSourceMap.find(HdTokens->displayOpacity); - if (it != _primvarSourceMap.end()) { - const VtValue& value = it->second.data; + if (itOpacity != primvarSourceMap.end()) { + const VtValue& value = itOpacity->second.data; if (value.IsHolding() && value.GetArraySize() > 0) { alphaArray = value.UncheckedGet(); - alphaInterp = it->second.interpolation; + alphaInterp = itOpacity->second.interpolation; } } @@ -826,43 +938,52 @@ void HdVP2Mesh::_UpdateDrawItem( alphaInterp == HdInterpolationConstant) { // Use fallback shader if there is no material binding or we // failed to create a shader instance from the material. - if (!stateToCommit._surfaceShader) { + if (!stateToCommit._shader) { const GfVec3f& color = colorArray[0]; - stateToCommit._surfaceShader = _delegate->GetFallbackShader( + + MHWRender::MShaderInstance* shader = _delegate->GetFallbackShader( MColor(color[0], color[1], color[2], alphaArray[0])); + if (shader != nullptr && shader != drawItemData._shader) { + drawItemData._shader = shader; + stateToCommit._shader = shader; + } } } else { if (!drawItemData._colorBuffer) { - const MHWRender::MVertexBufferDescriptor desc("", + const MHWRender::MVertexBufferDescriptor vbDesc("", MHWRender::MGeometry::kColor, MHWRender::MGeometry::kFloat, 4); drawItemData._colorBuffer.reset( - new MHWRender::MVertexBuffer(desc)); + new MHWRender::MVertexBuffer(vbDesc)); } void* bufferData = - drawItemData._colorBuffer->acquire(numVertices, writeOnly); + drawItemData._colorBuffer->acquire(numVertices, true); // Fill color and opacity into the float4 color stream. if (bufferData) { _FillPrimvarData(static_cast(bufferData), numVertices, 0, requiresUnsharedVertices, - id, _topology, HdTokens->displayColor, colorArray, colorInterp); + _rprimId, topology, HdTokens->displayColor, colorArray, colorInterp); _FillPrimvarData(static_cast(bufferData), numVertices, 3, requiresUnsharedVertices, - id, _topology, HdTokens->displayOpacity, alphaArray, alphaInterp); + _rprimId, topology, HdTokens->displayOpacity, alphaArray, alphaInterp); - stateToCommit._colorBufferData = static_cast(bufferData); + stateToCommit._colorBufferData = bufferData; } // Use fallback CPV shader if there is no material binding or // we failed to create a shader instance from the material. - if (!stateToCommit._surfaceShader) { - stateToCommit._surfaceShader = _delegate->GetFallbackCPVShader(); + if (!stateToCommit._shader) { + MHWRender::MShaderInstance* shader = _delegate->GetFallbackCPVShader(); + if (shader != nullptr && shader != drawItemData._shader) { + drawItemData._shader = shader; + stateToCommit._shader = shader; + } } } @@ -885,110 +1006,105 @@ void HdVP2Mesh::_UpdateDrawItem( } } - // Prepare primvar buffers required by the material. - if (material) { - for (const TfToken& pv : matPrimvars) { + // Prepare primvar buffers. + if ((desc.geomStyle == HdMeshGeomStyleHull) && + (itemDirtyBits & HdChangeTracker::DirtyPrimvar)) { + for (const auto& it : primvarSourceMap) { + const TfToken& token = it.first; // Color, opacity and normal have been prepared separately. - if ((pv == HdTokens->displayColor) || - (pv == HdTokens->displayOpacity) || - (pv == HdTokens->normals)) - continue; - - if (!HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, pv)) - continue; - - const auto it = _primvarSourceMap.find(pv); - if (it == _primvarSourceMap.end()) + if ((token == HdTokens->displayColor) || + (token == HdTokens->displayOpacity) || + (token == HdTokens->normals)) continue; - const VtValue& value = it->second.data; - const HdInterpolation& interp = it->second.interpolation; + const VtValue& value = it.second.data; + const HdInterpolation& interp = it.second.interpolation; if (!value.IsArrayValued() || value.GetArraySize() == 0) continue; MHWRender::MVertexBuffer* buffer = - drawItemData._primvarBuffers[pv].get(); + drawItemData._primvarBuffers[token].get(); void* bufferData = nullptr; if (value.IsHolding()) { if (!buffer) { - const MHWRender::MVertexBufferDescriptor desc("", + const MHWRender::MVertexBufferDescriptor vbDesc("", MHWRender::MGeometry::kTexture, MHWRender::MGeometry::kFloat, 1); - buffer = new MHWRender::MVertexBuffer(desc); - drawItemData._primvarBuffers[pv].reset(buffer); + buffer = new MHWRender::MVertexBuffer(vbDesc); + drawItemData._primvarBuffers[token].reset(buffer); } if (buffer) { - bufferData = buffer->acquire(numVertices, writeOnly); + bufferData = buffer->acquire(numVertices, true); if (bufferData) { _FillPrimvarData(static_cast(bufferData), numVertices, 0, requiresUnsharedVertices, - id, _topology, - pv, value.UncheckedGet(), interp); + _rprimId, topology, + token, value.UncheckedGet(), interp); } } } else if (value.IsHolding()) { if (!buffer) { - const MHWRender::MVertexBufferDescriptor desc("", + const MHWRender::MVertexBufferDescriptor vbDesc("", MHWRender::MGeometry::kTexture, MHWRender::MGeometry::kFloat, 2); - buffer = new MHWRender::MVertexBuffer(desc); - drawItemData._primvarBuffers[pv].reset(buffer); + buffer = new MHWRender::MVertexBuffer(vbDesc); + drawItemData._primvarBuffers[token].reset(buffer); } if (buffer) { - bufferData = buffer->acquire(numVertices, writeOnly); + bufferData = buffer->acquire(numVertices, true); if (bufferData) { _FillPrimvarData(static_cast(bufferData), numVertices, 0, requiresUnsharedVertices, - id, _topology, - pv, value.UncheckedGet(), interp); + _rprimId, topology, + token, value.UncheckedGet(), interp); } } } else if (value.IsHolding()) { if (!buffer) { - const MHWRender::MVertexBufferDescriptor desc("", + const MHWRender::MVertexBufferDescriptor vbDesc("", MHWRender::MGeometry::kTexture, MHWRender::MGeometry::kFloat, 3); - buffer = new MHWRender::MVertexBuffer(desc); - drawItemData._primvarBuffers[pv].reset(buffer); + buffer = new MHWRender::MVertexBuffer(vbDesc); + drawItemData._primvarBuffers[token].reset(buffer); } if (buffer) { - bufferData = buffer->acquire(numVertices, writeOnly); + bufferData = buffer->acquire(numVertices, true); if (bufferData) { _FillPrimvarData(static_cast(bufferData), numVertices, 0, requiresUnsharedVertices, - id, _topology, - pv, value.UncheckedGet(), interp); + _rprimId, topology, + token, value.UncheckedGet(), interp); } } } else if (value.IsHolding()) { if (!buffer) { - const MHWRender::MVertexBufferDescriptor desc("", + const MHWRender::MVertexBufferDescriptor vbDesc("", MHWRender::MGeometry::kTexture, MHWRender::MGeometry::kFloat, 4); - buffer = new MHWRender::MVertexBuffer(desc); - drawItemData._primvarBuffers[pv].reset(buffer); + buffer = new MHWRender::MVertexBuffer(vbDesc); + drawItemData._primvarBuffers[token].reset(buffer); } if (buffer) { - bufferData = buffer->acquire(numVertices, writeOnly); + bufferData = buffer->acquire(numVertices, true); if (bufferData) { _FillPrimvarData(static_cast(bufferData), numVertices, 0, requiresUnsharedVertices, - id, _topology, - pv, value.UncheckedGet(), interp); + _rprimId, topology, + token, value.UncheckedGet(), interp); } } } @@ -996,47 +1112,41 @@ void HdVP2Mesh::_UpdateDrawItem( TF_WARN("Unsupported primvar array"); } - stateToCommit._primvarBufferDataMap[pv] = static_cast(bufferData); + stateToCommit._primvarBufferDataMap[token] = bufferData; } } - // Bounding box is per-prim shared data. - GfRange3d range; + // The Maya API to update bounding box of a render item is expensive, so we + // update it only when it is expanded. + if (itemDirtyBits & HdChangeTracker::DirtyExtent) { + const GfRange3d& range = _sharedData.bounds.GetRange(); - if (HdChangeTracker::IsExtentDirty(*dirtyBits, id)) { - range = GetExtent(sceneDelegate); - if (!range.IsEmpty()) { - _sharedData.bounds.SetRange(range); - } + bool boundingBoxExpanded = false; - *dirtyBits &= ~HdChangeTracker::DirtyExtent; - } - else { - range = _sharedData.bounds.GetRange(); - } - - const GfVec3d& min = range.GetMin(); - const GfVec3d& max = range.GetMax(); - const MBoundingBox bounds(MPoint(min[0], min[1], min[2]), MPoint(max[0], max[1], max[2])); - - // World matrix is per-prim shared data. - GfMatrix4d transform; + const GfVec3d& min = range.GetMin(); + const MPoint pntMin(min[0], min[1], min[2]); + if (!drawItemData._boundingBox.contains(pntMin)) { + drawItemData._boundingBox.expand(pntMin); + boundingBoxExpanded = true; + } - if (HdChangeTracker::IsTransformDirty(*dirtyBits, id)) { - transform = sceneDelegate->GetTransform(id); - _sharedData.bounds.SetMatrix(transform); + const GfVec3d& max = range.GetMax(); + const MPoint pntMax(max[0], max[1], max[2]); + if (!drawItemData._boundingBox.contains(pntMax)) { + drawItemData._boundingBox.expand(pntMax); + boundingBoxExpanded = true; + } - *dirtyBits &= ~HdChangeTracker::DirtyTransform; - } - else { - transform = _sharedData.bounds.GetMatrix(); + if (boundingBoxExpanded) { + stateToCommit._boundingBox = &drawItemData._boundingBox; + } } - MMatrix worldMatrix; - transform.Get(worldMatrix.matrix); + MMatrix& worldMatrix = drawItemData._worldMatrix; + _sharedData.bounds.GetMatrix().Get(worldMatrix.matrix); - if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) { - _UpdateVisibility(sceneDelegate, dirtyBits); + if (itemDirtyBits & HdChangeTracker::DirtyTransform) { + stateToCommit._worldMatrix = &drawItemData._worldMatrix; } // If the mesh is instanced, create one new instance per transform. @@ -1047,15 +1157,21 @@ void HdVP2Mesh::_UpdateDrawItem( // Retrieve instance transforms from the instancer. HdInstancer *instancer = renderIndex.GetInstancer(GetInstancerId()); - VtMatrix4dArray transforms = - static_cast(instancer)-> + VtMatrix4dArray transforms = static_cast(instancer)-> ComputeInstanceTransforms(id); MMatrix instanceMatrix; - if ((desc.geomStyle == HdMeshGeomStyleHullEdgeOnly) && - !param->GetDrawScene().IsProxySelected()) { - if (auto state = param->GetDrawScene().GetPrimSelectionState(id)) { + if (drawItem->MatchesUsage(HdVP2DrawItem::kSelectionHighlight)) { + if (_selectionState == kFullySelected) { + stateToCommit._instanceTransforms.setLength(transforms.size()); + for (size_t i = 0; i < transforms.size(); ++i) { + transforms[i].Get(instanceMatrix.matrix); + instanceMatrix = worldMatrix * instanceMatrix; + stateToCommit._instanceTransforms[i] = instanceMatrix; + } + } + else if (auto state = drawScene.GetPrimSelectionState(id)) { for (const auto& indexArray : state->instanceIndices) { for (const auto index : indexArray) { transforms[index].Get(instanceMatrix.matrix); @@ -1066,105 +1182,170 @@ void HdVP2Mesh::_UpdateDrawItem( } } else { - for (size_t i = 0; i < transforms.size(); ++i) { + const unsigned int instanceCount = transforms.size(); + stateToCommit._instanceTransforms.setLength(instanceCount); + for (size_t i = 0; i < instanceCount; ++i) { transforms[i].Get(instanceMatrix.matrix); instanceMatrix = worldMatrix * instanceMatrix; - stateToCommit._instanceTransforms.append(instanceMatrix); + stateToCommit._instanceTransforms[i] = instanceMatrix; } - } - } - // Capture class member for lambda - MHWRender::MVertexBuffer* const positionsBuffer = _positionsBuffer.get(); + // If the item is used for both regular draw and selection highlight, + // it needs to display both wireframe color and selection highlight + // with one color vertex buffer. + if (drawItem->ContainsUsage(HdVP2DrawItem::kSelectionHighlight)) { + const MColor& color = drawScene.GetWireframeColor(); + unsigned int offset = 0; + + stateToCommit._instanceColors.setLength(instanceCount * kNumColorChannels); + for (unsigned int i = 0; i < instanceCount; ++i) { + for (unsigned int j = 0; j < kNumColorChannels; j++) { + stateToCommit._instanceColors[offset++] = color[j]; + } + } - _delegate->GetVP2ResourceRegistry().EnqueueCommit( - [drawItem, stateToCommit, param, positionsBuffer, bounds, worldMatrix]() - { - const MString& renderItemName = drawItem->GetRenderItemName(); + if (auto state = drawScene.GetPrimSelectionState(id)) { + for (const auto& indexArray : state->instanceIndices) { + for (const auto i : indexArray) { + offset = i * kNumColorChannels; + for (unsigned int j = 0; j < kNumColorChannels; j++) { + stateToCommit._instanceColors[offset+j] = kSelectionHighlightColor[j]; + } + } + } + } + } + } + } + else { + // Non-instanced Rprims. + if (itemDirtyBits & DirtySelectionHighlight) { + if (drawItem->ContainsUsage(HdVP2DrawItem::kRegular) && + drawItem->ContainsUsage(HdVP2DrawItem::kSelectionHighlight)) { + MHWRender::MShaderInstance* shader = _delegate->Get3dSolidShader( + (_selectionState != kUnselected) ? + kSelectionHighlightColor : + drawScene.GetWireframeColor() + ); + + if (shader != nullptr && shader != drawItemData._shader) { + drawItemData._shader = shader; + stateToCommit._shader = shader; + stateToCommit._isTransparent = false; + } + } + } + } - MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, - MProfiler::kColorC_L2, renderItemName.asChar(), "HdVP2Mesh Commit Buffers"); + if (itemDirtyBits & HdChangeTracker::DirtyVisibility) { + drawItemData._enabled = drawItem->GetVisible(); + stateToCommit._enabled = &drawItemData._enabled; + } - MHWRender::MRenderItem* renderItem = param->GetContainer()->find(renderItemName); - if(!renderItem) { - TF_CODING_ERROR("Invalid render item: %s\n", renderItemName.asChar()); - return; + if (drawItem->MatchesUsage(HdVP2DrawItem::kSelectionHighlight)) { + if (itemDirtyBits & DirtySelectionHighlight) { + drawItemData._enabled = + (_selectionState != kUnselected) && drawItem->GetVisible(); + stateToCommit._enabled = &drawItemData._enabled; } + } - MHWRender::MVertexBuffer* colorBuffer = stateToCommit._drawItemData._colorBuffer.get(); - MHWRender::MVertexBuffer* normalsBuffer = stateToCommit._drawItemData._normalsBuffer.get(); - MHWRender::MIndexBuffer* indexBuffer = stateToCommit._drawItemData._indexBuffer.get(); + // Reset dirty bits because we've prepared commit state for this draw item. + drawItem->ResetDirtyBits(); - HdVP2DrawItem::PrimvarBufferMap& primvarBuffers = stateToCommit._drawItemData._primvarBuffers; + // Capture class member for lambda + MHWRender::MVertexBuffer* const positionsBuffer = _meshSharedData._positionsBuffer.get(); - MHWRender::MVertexBufferArray vertexBuffers; - vertexBuffers.addBuffer("positions", positionsBuffer); + _delegate->GetVP2ResourceRegistry().EnqueueCommit( + [drawItem, stateToCommit, param, positionsBuffer]() + { + MHWRender::MRenderItem* renderItem = drawItem->GetRenderItem(); + if (ARCH_UNLIKELY(!renderItem)) + return; - if (colorBuffer) - vertexBuffers.addBuffer("diffuseColor", colorBuffer); + MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, + MProfiler::kColorC_L2, drawItem->GetRenderItemName().asChar(), "Commit"); - if (normalsBuffer) - vertexBuffers.addBuffer("normals", normalsBuffer); + const HdVP2DrawItem::RenderItemData& drawItemData = stateToCommit._drawItemData; - for (auto& entry : primvarBuffers) { - const TfToken& primvarName = entry.first; - MHWRender::MVertexBuffer* primvarBuffer = entry.second.get(); - if (primvarBuffer) { - vertexBuffers.addBuffer(primvarName.GetText(), primvarBuffer); - } - } + MHWRender::MVertexBuffer* colorBuffer = drawItemData._colorBuffer.get(); + MHWRender::MVertexBuffer* normalsBuffer = drawItemData._normalsBuffer.get(); + MHWRender::MIndexBuffer* indexBuffer = drawItemData._indexBuffer.get(); - // If available, something changed - if(stateToCommit._positionBufferData) - positionsBuffer->commit(stateToCommit._positionBufferData); + const HdVP2DrawItem::PrimvarBufferMap& primvarBuffers = drawItemData._primvarBuffers; // If available, something changed - if (stateToCommit._colorBufferData && colorBuffer) + if (stateToCommit._colorBufferData) colorBuffer->commit(stateToCommit._colorBufferData); // If available, something changed - if (stateToCommit._normalsBufferData && normalsBuffer) + if (stateToCommit._normalsBufferData) normalsBuffer->commit(stateToCommit._normalsBufferData); // If available, something changed for (const auto& entry : stateToCommit._primvarBufferDataMap) { const TfToken& primvarName = entry.first; - float* primvarBufferData = entry.second; - - const auto it = primvarBuffers.find(primvarName); - if (it != primvarBuffers.end()) { - MHWRender::MVertexBuffer* primvarBuffer = it->second.get(); - if (primvarBuffer && primvarBufferData) { - primvarBuffer->commit(primvarBufferData); + void* primvarBufferData = entry.second; + if (primvarBufferData) { + const auto it = primvarBuffers.find(primvarName); + if (it != primvarBuffers.end()) { + MHWRender::MVertexBuffer* primvarBuffer = it->second.get(); + if (primvarBuffer) { + primvarBuffer->commit(primvarBufferData); + } } } } // If available, something changed - if (stateToCommit._indexBufferData && indexBuffer) + if (stateToCommit._indexBufferData) indexBuffer->commit(stateToCommit._indexBufferData); // If available, something changed - if (stateToCommit._surfaceShader != nullptr && - stateToCommit._surfaceShader != renderItem->getShader()) { - renderItem->setShader(stateToCommit._surfaceShader); + if (stateToCommit._shader != nullptr) { + renderItem->setShader(stateToCommit._shader); renderItem->setTreatAsTransparent(stateToCommit._isTransparent); } - if ((stateToCommit._dirtyBits & HdChangeTracker::DirtyVisibility) != 0) { - renderItem->enable(drawItem->IsEnabled() && drawItem->GetVisible()); + // If the enable state is changed, then update it. + if (stateToCommit._enabled != nullptr) { + renderItem->enable(*stateToCommit._enabled); } ProxyRenderDelegate& drawScene = param->GetDrawScene(); - drawScene.setGeometryForRenderItem(*renderItem, vertexBuffers, *indexBuffer, &bounds); + + // Associate geometries with the render item only if the shader or the + // bounding box is changed. + if (stateToCommit._shader != nullptr || + stateToCommit._boundingBox != nullptr) { + MHWRender::MVertexBufferArray vertexBuffers; + vertexBuffers.addBuffer(kPositionsStr, positionsBuffer); + + if (colorBuffer) + vertexBuffers.addBuffer(kDiffuseColorStr, colorBuffer); + + if (normalsBuffer) + vertexBuffers.addBuffer(kNormalsStr, normalsBuffer); + + for (auto& entry : primvarBuffers) { + const TfToken& primvarName = entry.first; + MHWRender::MVertexBuffer* primvarBuffer = entry.second.get(); + if (primvarBuffer) { + vertexBuffers.addBuffer(primvarName.GetText(), primvarBuffer); + } + } + + drawScene.setGeometryForRenderItem(*renderItem, + vertexBuffers, *indexBuffer, stateToCommit._boundingBox); + } // Important, update instance transforms after setting geometry on render items! auto& oldInstanceCount = stateToCommit._drawItemData._instanceCount; auto newInstanceCount = stateToCommit._instanceTransforms.length(); - if (oldInstanceCount > 1) { - // GPU instancing has been enabled. We cannot switch to consolidation - // without recreating render item, so we keep using GPU instancing. + // GPU instancing has been enabled. We cannot switch to consolidation + // without recreating render item, so we keep using GPU instancing. + if (stateToCommit._drawItemData._usingInstancedDraw) { if (oldInstanceCount == newInstanceCount) { for (unsigned int i = 0; i < newInstanceCount; i++) { // VP2 defines instance ID of the first instance to be 1. @@ -1175,6 +1356,12 @@ void HdVP2Mesh::_UpdateDrawItem( drawScene.setInstanceTransformArray(*renderItem, stateToCommit._instanceTransforms); } + + if (stateToCommit._instanceColors.length() == + newInstanceCount * kNumColorChannels) { + drawScene.setExtraInstanceData(*renderItem, + kSolidColorStr, stateToCommit._instanceColors); + } } else if (newInstanceCount > 1) { // Turn off consolidation to allow GPU instancing to be used for @@ -1182,37 +1369,47 @@ void HdVP2Mesh::_UpdateDrawItem( setWantConsolidation(*renderItem, false); drawScene.setInstanceTransformArray(*renderItem, stateToCommit._instanceTransforms); + + if (stateToCommit._instanceColors.length() == + newInstanceCount * kNumColorChannels) { + drawScene.setExtraInstanceData(*renderItem, + kSolidColorStr, stateToCommit._instanceColors); + } + + stateToCommit._drawItemData._usingInstancedDraw = true; } else if (newInstanceCount == 1) { // Special case for single instance prims. We will keep the original // render item to allow consolidation. renderItem->setMatrix(&stateToCommit._instanceTransforms[0]); } - else { + else if (stateToCommit._worldMatrix != nullptr) { // Regular non-instanced prims. Consolidation has been turned on by // default and will be kept enabled on this case. - renderItem->setMatrix(&worldMatrix); + renderItem->setMatrix(stateToCommit._worldMatrix); } oldInstanceCount = newInstanceCount; }); } +/*! \brief Update _primvarSourceMap, our local cache of raw primvar data. + + This function pulls data from the scene delegate, but defers processing. + + While iterating primvars, we skip "points" (vertex positions) because + the points primvar is processed separately for direct access later. We + only call GetPrimvar on primvars that have been marked dirty. +*/ void HdVP2Mesh::_UpdatePrimvarSources( HdSceneDelegate* sceneDelegate, - HdDirtyBits dirtyBits) + HdDirtyBits dirtyBits, + const TfTokenVector& requiredPrimvars) { const SdfPath& id = GetId(); - // Update _primvarSourceMap, our local cache of raw primvar data. - // This function pulls data from the scene delegate, but defers processing. - // - // While iterating primvars, we skip "points" (vertex positions) because - // the points primvar is processed separately for direct access later. We - // only call GetPrimvar on primvars that have been marked dirty. - // - // Currently, hydra doesn't have a good way of communicating changes in - // the set of primvars, so we only ever add and update to the primvar set. + TfTokenVector::const_iterator begin = requiredPrimvars.cbegin(); + TfTokenVector::const_iterator end = requiredPrimvars.cend(); for (size_t i = 0; i < HdInterpolationCount; i++) { const HdInterpolation interp = static_cast(i); @@ -1220,10 +1417,14 @@ void HdVP2Mesh::_UpdatePrimvarSources( GetPrimvarDescriptors(sceneDelegate, interp); for (const HdPrimvarDescriptor& pv: primvars) { - if (HdChangeTracker::IsPrimvarDirty(dirtyBits, id, pv.name) && - pv.name != HdTokens->points) { - const VtValue value = GetPrimvar(sceneDelegate, pv.name); - _primvarSourceMap[pv.name] = { value, interp }; + if (std::find(begin, end, pv.name) != end) { + if (HdChangeTracker::IsPrimvarDirty(dirtyBits, id, pv.name)) { + const VtValue value = GetPrimvar(sceneDelegate, pv.name); + _meshSharedData._primvarSourceMap[pv.name] = { value, interp }; + } + } + else { + _meshSharedData._primvarSourceMap.erase(pv.name); } } } @@ -1255,7 +1456,8 @@ MHWRender::MRenderItem* HdVP2Mesh::_CreatePointsRenderItem(const MString& name) /*! \brief Create render item for wireframe repr. */ -MHWRender::MRenderItem* HdVP2Mesh::_CreateWireframeRenderItem(const MString& name) const +MHWRender::MRenderItem* HdVP2Mesh::_CreateWireframeRenderItem( + const MString& name) const { MHWRender::MRenderItem* const renderItem = MHWRender::MRenderItem::Create( name, @@ -1263,12 +1465,12 @@ MHWRender::MRenderItem* HdVP2Mesh::_CreateWireframeRenderItem(const MString& nam MHWRender::MGeometry::kLines ); - renderItem->depthPriority(MHWRender::MRenderItem::sActiveWireDepthPriority); + renderItem->setDrawMode(MHWRender::MGeometry::kWireframe); + renderItem->depthPriority(MHWRender::MRenderItem::sDormantWireDepthPriority); renderItem->castsShadows(false); renderItem->receivesShadows(false); - renderItem->setShader(_delegate->Get3dSolidShader()); - - renderItem->setSelectionMask(MSelectionMask()); + renderItem->setShader(_delegate->Get3dSolidShader(kOpaqueBlue)); + renderItem->setSelectionMask(MSelectionMask::kSelectMeshes); setWantConsolidation(*renderItem, true); @@ -1285,11 +1487,14 @@ MHWRender::MRenderItem* HdVP2Mesh::_CreateSmoothHullRenderItem(const MString& na MHWRender::MGeometry::kTriangles ); + constexpr MHWRender::MGeometry::DrawMode drawMode = + static_cast( + MHWRender::MGeometry::kShaded | MHWRender::MGeometry::kTextured); + renderItem->setDrawMode(drawMode); renderItem->setExcludedFromPostEffects(false); renderItem->castsShadows(true); renderItem->receivesShadows(true); - renderItem->setShader(_delegate->GetFallbackShader()); - + renderItem->setShader(_delegate->GetFallbackShader(kOpaqueGray)); renderItem->setSelectionMask(MSelectionMask::kSelectMeshes); setWantConsolidation(*renderItem, true); @@ -1297,62 +1502,30 @@ MHWRender::MRenderItem* HdVP2Mesh::_CreateSmoothHullRenderItem(const MString& na return renderItem; } -/*! \brief Create render item for the specified desc. +/*! \brief Create render item to support selection highlight for smoothHull repr. */ -MHWRender::MRenderItem* HdVP2Mesh::_CreateRenderItem( - const MString& name, - const HdMeshReprDesc& desc) const +MHWRender::MRenderItem* HdVP2Mesh::_CreateSelectionHighlightRenderItem( + const MString& name) const { - MHWRender::MRenderItem* renderItem = nullptr; - - switch (desc.geomStyle) { - case HdMeshGeomStyleHull: - renderItem = _CreateSmoothHullRenderItem(name); - break; - case HdMeshGeomStyleHullEdgeOnly: - renderItem = _CreateWireframeRenderItem(name); - break; - case HdMeshGeomStylePoints: - renderItem = _CreatePointsRenderItem(name); - break; - default: - TF_WARN("Unexpected geomStyle"); - break; - } - - return renderItem; -} - -/*! \brief Enable or disable the wireframe draw item. - - \return True if no draw items should be created for the repr. -*/ -bool HdVP2Mesh::_EnableWireDrawItems( - const HdReprSharedPtr& repr, - HdDirtyBits* dirtyBits, - bool enable) -{ - if (_wireItemsEnabled == enable) { - return true; - } + MHWRender::MRenderItem* const renderItem = MHWRender::MRenderItem::Create( + name, + MHWRender::MRenderItem::DecorationItem, + MHWRender::MGeometry::kLines + ); - _wireItemsEnabled = enable; + constexpr MHWRender::MGeometry::DrawMode drawMode = + static_cast( + MHWRender::MGeometry::kShaded | MHWRender::MGeometry::kTextured); + renderItem->setDrawMode(drawMode); + renderItem->depthPriority(MHWRender::MRenderItem::sActiveWireDepthPriority); + renderItem->castsShadows(false); + renderItem->receivesShadows(false); + renderItem->setShader(_delegate->Get3dSolidShader(kSelectionHighlightColor)); + renderItem->setSelectionMask(MSelectionMask()); - if (repr) { - const HdRepr::DrawItems& items = repr->GetDrawItems(); - for (HdDrawItem* item : items) { - if (auto drawItem = static_cast(item)) { - const HdMeshReprDesc& reprDesc = drawItem->GetReprDesc(); - if (reprDesc.geomStyle == HdMeshGeomStyleHullEdgeOnly) { - drawItem->Enable(enable); - *dirtyBits |= HdChangeTracker::DirtyVisibility; - return true; - } - } - } - } + setWantConsolidation(*renderItem, true); - return false; + return renderItem; } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/mesh.h b/lib/render/vp2RenderDelegate/mesh.h index 38c34810eb..ed5a5ad7e9 100644 --- a/lib/render/vp2RenderDelegate/mesh.h +++ b/lib/render/vp2RenderDelegate/mesh.h @@ -20,15 +20,49 @@ #include "pxr/pxr.h" #include "pxr/imaging/hd/mesh.h" -#include "render_delegate.h" - #include +#include "proxyRenderDelegate.h" + PXR_NAMESPACE_OPEN_SCOPE class HdSceneDelegate; class HdVP2DrawItem; -struct HdMeshReprDesc; +class HdVP2RenderDelegate; + +/*! \brief HdVP2Mesh-specific data shared among all its draw items. + \class HdVP2MeshSharedData + + A Rprim can have multiple draw items. The shared data are extracted from + USD scene delegate during synchronization. Then each draw item can prepare + draw data from these shared data as needed. +*/ +struct HdVP2MeshSharedData { + //! Cached scene data. VtArrays are reference counted, so as long as we + //! only call const accessors keeping them around doesn't incur a buffer + //! copy. + HdMeshTopology _topology; + + //! Optional topology which is computed for conversion from shared vertices + //! to unshared when needed. + std::unique_ptr _unsharedTopology; + + //! A local cache of primvar scene data. "data" is a copy-on-write handle to + //! the actual primvar buffer, and "interpolation" is the interpolation mode + //! to be used. + struct PrimvarSource { + VtValue data; + HdInterpolation interpolation; + }; + TfHashMap _primvarSourceMap; + + //! A local cache of points. It is not cached in the above primvar map + //! but a separate VtArray for easier access. + VtVec3fArray _points; + + //!< Position buffer of the Rprim to be shared among all its draw items. + std::unique_ptr _positionsBuffer; +}; /*! \brief VP2 representation of poly-mesh object. \class HdVP2Mesh @@ -61,17 +95,18 @@ class HdVP2Mesh final : public HdMesh { void _InitRepr(const TfToken&, HdDirtyBits*) override; - void _UpdateRepr(HdSceneDelegate*, const TfToken&, HdDirtyBits*); + void _UpdateRepr(HdSceneDelegate*, const TfToken&); void _UpdateDrawItem( - HdSceneDelegate*, HdVP2DrawItem*, HdDirtyBits*, - const HdMeshReprDesc&, bool requireSmoothNormals, bool requireFlatNormals); + HdSceneDelegate*, HdVP2DrawItem*, + bool requireSmoothNormals, bool requireFlatNormals); - bool _EnableWireDrawItems(const HdReprSharedPtr& repr, HdDirtyBits* dirtyBits, bool enable); + void _UpdatePrimvarSources( + HdSceneDelegate* sceneDelegate, + HdDirtyBits dirtyBits, + const TfTokenVector& requiredPrimvars); - void _UpdatePrimvarSources(HdSceneDelegate* sceneDelegate, HdDirtyBits dirtyBits); - - MHWRender::MRenderItem* _CreateRenderItem(const MString& name, const HdMeshReprDesc& desc) const; + MHWRender::MRenderItem* _CreateSelectionHighlightRenderItem(const MString& name) const; MHWRender::MRenderItem* _CreateSmoothHullRenderItem(const MString& name) const; MHWRender::MRenderItem* _CreateWireframeRenderItem(const MString& name) const; MHWRender::MRenderItem* _CreatePointsRenderItem(const MString& name) const; @@ -83,30 +118,15 @@ class HdVP2Mesh final : public HdMesh { DirtyIndices = (DirtyFlatNormals << 1), DirtyHullIndices = (DirtyIndices << 1), DirtyPointsIndices = (DirtyHullIndices << 1), - DirtyBoundingBox = (DirtyPointsIndices << 1) + DirtySelection = (DirtyPointsIndices << 1), + DirtySelectionHighlight = (DirtySelection << 1) }; HdVP2RenderDelegate* _delegate{ nullptr }; //!< VP2 render delegate for which this mesh was created HdDirtyBits _customDirtyBitsInUse{ 0 }; //!< Storage for custom dirty bits. See _PropagateDirtyBits for details. - - // TODO: Define HdVP2MeshSharedData to hold extra shared data specific to VP2? - std::unique_ptr _positionsBuffer; //!< Per-Rprim position buffer to be shared among render items - bool _wireItemsEnabled{ false }; //!< Whether draw items for the wire repr are enabled - - //! Cached scene data. VtArrays are reference counted, so as long as we - //! only call const accessors keeping them around doesn't incur a buffer - //! copy. - HdMeshTopology _topology; - VtVec3fArray _points; - - //! A local cache of primvar scene data. "data" is a copy-on-write handle to - //! the actual primvar buffer, and "interpolation" is the interpolation mode - //! to be used. - struct PrimvarSource { - VtValue data; - HdInterpolation interpolation; - }; - TfHashMap _primvarSourceMap; + const MString _rprimId; //!< Rprim id cached as a maya string for easier debugging and profiling + HdVP2MeshSharedData _meshSharedData; //!< Shared data for all draw items of the Rprim + HdVP2SelectionStatus _selectionState{ kUnselected };//!< Selection status of the Rprim }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp b/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp index 041cb1a308..ad713c0ba5 100644 --- a/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp +++ b/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp @@ -22,6 +22,7 @@ #include "pxr/imaging/hdx/renderTask.h" #include "pxr/imaging/hdx/selectionTracker.h" #include "pxr/imaging/hdx/taskController.h" +#include "pxr/imaging/hdx/tokens.h" #include "pxr/imaging/hd/enums.h" #include "pxr/imaging/hd/mesh.h" #include "pxr/imaging/hd/repr.h" @@ -34,6 +35,7 @@ #include #include #include +#include #include #include @@ -49,6 +51,15 @@ PXR_NAMESPACE_OPEN_SCOPE namespace { + //! Representation selector for shaded and textured viewport mode + const HdReprSelector kSmoothHullReprSelector(HdReprTokens->smoothHull); + + //! Representation selector for wireframe viewport mode + const HdReprSelector kWireReprSelector(TfToken(), HdReprTokens->wire); + + //! Representation selector for point snapping + const HdReprSelector kPointsReprSelector(TfToken(), TfToken(), HdReprTokens->points); + MGlobal::ListAdjustment GetListAdjustment() { // Keyboard modifiers can be queried from QApplication::keyboardModifiers() @@ -79,6 +90,28 @@ namespace return listAdjustment; } + //! \brief Configure repr descriptions + void _ConfigureReprs() + { + // One desc for shaded display, the other desc for selection highlight. + HdMesh::ConfigureRepr(HdReprTokens->smoothHull, + HdMeshReprDesc(HdMeshGeomStyleHull, + HdCullStyleDontCare, + HdMeshReprDescTokens->surfaceShader, + /*flatShadingEnabled=*/false, + /*blendWireframeColor=*/false), + HdMeshReprDesc(HdMeshGeomStyleHullEdgeOnly, + HdCullStyleDontCare, + HdMeshReprDescTokens->surfaceShader, + /*flatShadingEnabled=*/false, + /*blendWireframeColor=*/false) + ); + + // Special token for selection update and no need to create repr. Adding + // the empty desc to remove Hydra warning. + HdMesh::ConfigureRepr(HdxTokens->selection, HdMeshReprDesc()); + } + #if defined(WANT_UFE_BUILD) class UfeSelectionObserver : public Ufe::Observer { @@ -107,6 +140,14 @@ namespace private: ProxyRenderDelegate& _proxyRenderDelegate; }; +#else + void SelectionChangedCB(void* data) + { + ProxyRenderDelegate* prd = static_cast(data); + if (prd) { + prd->SelectionChanged(); + } + } #endif } // namespace @@ -133,6 +174,12 @@ ProxyRenderDelegate::~ProxyRenderDelegate() { delete _taskController; delete _renderIndex; delete _renderDelegate; + +#if !defined(WANT_UFE_BUILD) + if (_mayaSelectionCallbackId != 0) { + MMessage::removeCallback(_mayaSelectionCallbackId); + } +#endif } //! \brief This drawing routine supports all devices (DirectX and OpenGL) @@ -184,6 +231,10 @@ void ProxyRenderDelegate::_InitRenderDelegate() { MProfilingScope subProfilingScope(HdVP2RenderDelegate::sProfilerCategory, MProfiler::kColorD_L1, "Allocate RenderIndex"); _renderIndex = HdRenderIndex::New(_renderDelegate); + + // Add additional configurations after render index creation. + static std::once_flag reprsOnce; + std::call_once(reprsOnce, _ConfigureReprs); } if (!_sceneDelegate) { @@ -198,24 +249,8 @@ void ProxyRenderDelegate::_InitRenderDelegate() { _taskController = new HdxTaskController(_renderIndex, delegateID.AppendChild(TfToken(TfStringPrintf("_UsdImaging_VP2_%p", this))) ); - // Assign a collection of active representations, then each Rprim will - // initialize and update draw data for these active repr(s). If update - // for selection is enabled, the draw data for the "points" repr won't - // be prepared until point snapping is activated; otherwise they have - // to be early prepared for potential activation of point snapping. - _defaultCollection.reset(new HdRprimCollection(HdTokens->geometry, -#if defined(MAYA_ENABLE_UPDATE_FOR_SELECTION) - HdReprSelector(HdReprTokens->smoothHull) -#else - HdReprSelector(HdReprTokens->smoothHull, TfToken(), HdReprTokens->points) -#endif - )); - - _taskController->SetCollection(*_defaultCollection); - - _selectionHighlightCollection.reset(new HdRprimCollection(HdTokens->geometry, - HdReprSelector(HdReprTokens->wire) - )); + _defaultCollection.reset(new HdRprimCollection()); + _defaultCollection->SetName(HdTokens->geometry); _selection.reset(new HdSelection); @@ -227,6 +262,10 @@ void ProxyRenderDelegate::_InitRenderDelegate() { globalSelection->addObserver(_ufeSelectionObserver); } } +#else + // Without UFE, support basic selection highlight at proxy shape level. + _mayaSelectionCallbackId = MEventMessage::addEventCallback( + "SelectionChanged", SelectionChangedCB, this); #endif // We don't really need any HdTask because VP2RenderDelegate uses Hydra @@ -283,53 +322,65 @@ void ProxyRenderDelegate::_UpdateTime() { } } -//! \brief Execute Hydra engine which will performe minimal update VP2 state update based on change tracker. -void ProxyRenderDelegate::_Execute(const MHWRender::MFrameContext& frameContext) { +//! \brief Execute Hydra engine to perform minimal VP2 draw data update based on change tracker. +void ProxyRenderDelegate::_Execute(const MHWRender::MFrameContext& frameContext) +{ MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, MProfiler::kColorC_L1, "Execute"); + // If update for selection is enabled, the draw data for the "points" repr + // won't be prepared until point snapping is activated; otherwise the draw + // data have to be prepared early for possible activation of point snapping. #if defined(MAYA_ENABLE_UPDATE_FOR_SELECTION) - // Since Maya 2020, subscene update can be invoked in a selection pass. + HdReprSelector reprSelector; + const bool inSelectionPass = (frameContext.getSelectionInfo() != nullptr); const bool inPointSnapping = pointSnappingActive(); // Query selection adjustment mode only if the update is triggered in a selection pass. _globalListAdjustment = (inSelectionPass && !inPointSnapping) ? GetListAdjustment() : MGlobal::kReplaceList; #else - // Before Maya 2020, subscene update would never be invoked in a selection pass. + HdReprSelector reprSelector = kPointsReprSelector; + constexpr bool inSelectionPass = false; constexpr bool inPointSnapping = false; #endif - // HdC_TODO: we need to figure out how to use Hydra repr more effectively - // when more reprs are coming. if (inSelectionPass) { - if (inPointSnapping) { - // Update display reprs at the first frame where point snapping is activated. - if (!_defaultCollection->GetReprSelector().Contains(HdReprTokens->points)) { - const HdReprSelector under(TfToken(), TfToken(), HdReprTokens->points); - const HdReprSelector reprSelector = - _defaultCollection->GetReprSelector().CompositeOver(under); - _defaultCollection->SetReprSelector(reprSelector); - _taskController->SetCollection(*_defaultCollection); - } + if (inPointSnapping && !reprSelector.Contains(HdReprTokens->points)) { + reprSelector = reprSelector.CompositeOver(kPointsReprSelector); } } - else if (_selectionChanged) { - _selectionChanged = false; - - if (_UpdateSelectionHighlight()) { - // Update display reprs at the first frame where selection highlight is activated. - if (!_defaultCollection->GetReprSelector().Contains(HdReprTokens->wire)) { - const HdReprSelector under(TfToken(), HdReprTokens->wire, TfToken()); - const HdReprSelector reprSelector = - _defaultCollection->GetReprSelector().CompositeOver(under); - _defaultCollection->SetReprSelector(reprSelector); - _taskController->SetCollection(*_defaultCollection); + else { + if (_selectionChanged) { + _UpdateSelectionStates(); + _selectionChanged = false; + } + + const unsigned int displayStyle = frameContext.getDisplayStyle(); + + if (displayStyle & MHWRender::MFrameContext::kGouraudShaded) { + if (!reprSelector.Contains(HdReprTokens->smoothHull)) { + reprSelector = reprSelector.CompositeOver(kSmoothHullReprSelector); + } + } + + if (displayStyle & MHWRender::MFrameContext::kWireFrame) { + if (!reprSelector.Contains(HdReprTokens->wire)) { + reprSelector = reprSelector.CompositeOver(kWireReprSelector); } + + MDagPath proxyDagPath; + MDagPath::getAPathTo(_mObject, proxyDagPath); + _wireframeColor = MHWRender::MGeometryUtilities::wireframeColor(proxyDagPath); } } + if (_defaultCollection->GetReprSelector() != reprSelector) { + _defaultCollection->SetReprSelector(reprSelector); + _taskController->SetCollection(*_defaultCollection); + } + _engine.Execute(_renderIndex, &_dummyTasks); } @@ -490,35 +541,29 @@ void ProxyRenderDelegate::_FilterSelection() #endif } -/*! \brief Update for selection highlight - - \return True if selection highlight needs to be shown. +/*! \brief Notify selection change to rprims. */ -bool ProxyRenderDelegate::_UpdateSelectionHighlight() +void ProxyRenderDelegate::_UpdateSelectionStates() { - bool retVal = false; - - const bool wasProxySelected = _isProxySelected; - MDagPath proxyDagPath; MDagPath::getAPathTo(_mObject, proxyDagPath); auto status = MHWRender::MGeometryUtilities::displayStatus(proxyDagPath); - _isProxySelected = ((status == MHWRender::kHilite) || (status == MHWRender::kLead)); - constexpr HdSelection::HighlightMode mode = HdSelection::HighlightModeSelect; + const bool wasProxySelected = _isProxySelected; + _isProxySelected = ((status == MHWRender::kHilite) || (status == MHWRender::kLead)); SdfPathVector rootPaths; if (_isProxySelected) { rootPaths.push_back(SdfPath::AbsoluteRootPath()); - retVal = true; } else if (wasProxySelected) { rootPaths.push_back(SdfPath::AbsoluteRootPath()); _FilterSelection(); - retVal = !_selection->GetSelectedPrimPaths(mode).empty(); } else { + constexpr HdSelection::HighlightMode mode = HdSelection::HighlightModeSelect; + SdfPathVector oldPaths = _selection->GetSelectedPrimPaths(mode); _FilterSelection(); SdfPathVector newPaths = _selection->GetSelectedPrimPaths(mode); @@ -528,28 +573,16 @@ bool ProxyRenderDelegate::_UpdateSelectionHighlight() rootPaths.reserve(rootPaths.size() + newPaths.size()); rootPaths.insert(rootPaths.end(), newPaths.begin(), newPaths.end()); } - - retVal = !newPaths.empty(); } if (!rootPaths.empty()) { - _inSelectionHighlightUpdate = true; - - _selectionHighlightCollection->SetRootPaths(rootPaths); - _taskController->SetCollection(*_selectionHighlightCollection); + HdRprimCollection collection(HdTokens->geometry, + HdReprSelector(HdxTokens->selection)); + collection.SetRootPaths(rootPaths); + _taskController->SetCollection(collection); _engine.Execute(_renderIndex, &_dummyTasks); _taskController->SetCollection(*_defaultCollection); - - _inSelectionHighlightUpdate = false; } - - return retVal; -} - -//! \brief Query whether the proxy is selected. -bool ProxyRenderDelegate::IsProxySelected() const -{ - return _isProxySelected; } //! \brief Query the selection state of a given prim. @@ -560,4 +593,26 @@ ProxyRenderDelegate::GetPrimSelectionState(const SdfPath& path) const _selection->GetPrimSelectionState(HdSelection::HighlightModeSelect, path); } +//! \brief Query the selection status of a given prim. +HdVP2SelectionStatus +ProxyRenderDelegate::GetPrimSelectionStatus(const SdfPath& path) const +{ + if (_isProxySelected) { + return kFullySelected; + } + + const HdSelection::PrimSelectionState* state = GetPrimSelectionState(path); + if (state && state->fullySelected) { + return kFullySelected; + } + + return state ? kPartiallySelected : kUnselected; +} + +//! \brief Query the wireframe color assigned to the proxy shape. +const MColor& ProxyRenderDelegate::GetWireframeColor() const +{ + return _wireframeColor; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/proxyRenderDelegate.h b/lib/render/vp2RenderDelegate/proxyRenderDelegate.h index 3ed998cf8b..c02944c29e 100644 --- a/lib/render/vp2RenderDelegate/proxyRenderDelegate.h +++ b/lib/render/vp2RenderDelegate/proxyRenderDelegate.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,14 @@ class UsdImagingDelegate; class MayaUsdProxyShapeBase; class HdxTaskController; +/*! \brief Enumerations for selection status +*/ +enum HdVP2SelectionStatus { + kUnselected = 0, //!< The Rprim is not selected + kPartiallySelected = 1, //!< The Rprim is partially selected (only applicable for instanced Rprims) + kFullySelected = 2 //!< The Rprim is selected (meaning fully selected for instanced Rprims) +}; + /*! \brief USD Proxy rendering routine via VP2 MPxSubSceneOverride This drawing routine leverages HdVP2RenderDelegate for synchronization @@ -110,13 +119,13 @@ class ProxyRenderDelegate : public MHWRender::MPxSubSceneOverride void SelectionChanged(); MAYAUSD_CORE_PUBLIC - bool IsProxySelected() const; + const MColor& GetWireframeColor() const; MAYAUSD_CORE_PUBLIC - bool InSelectionHighlightUpdate() const { return _inSelectionHighlightUpdate; } + const HdSelection::PrimSelectionState* GetPrimSelectionState(const SdfPath& path) const; MAYAUSD_CORE_PUBLIC - const HdSelection::PrimSelectionState* GetPrimSelectionState(const SdfPath& path) const; + HdVP2SelectionStatus GetPrimSelectionStatus(const SdfPath& path) const; private: ProxyRenderDelegate(const ProxyRenderDelegate&) = delete; @@ -130,7 +139,7 @@ class ProxyRenderDelegate : public MHWRender::MPxSubSceneOverride bool _isInitialized(); void _FilterSelection(); - bool _UpdateSelectionHighlight(); + void _UpdateSelectionStates(); MObject _mObject; //!< Proxy shape MObject @@ -148,20 +157,20 @@ class ProxyRenderDelegate : public MHWRender::MPxSubSceneOverride bool _isPopulated{ false }; //!< If false, scene delegate wasn't populated yet within render index bool _selectionChanged{ false }; //!< Whether there is any selection change or not bool _isProxySelected{ false }; //!< Whether the proxy shape is selected - bool _inSelectionHighlightUpdate{ false };//!< Set to true when selection highlight update is executing + MColor _wireframeColor; //!< Wireframe color assigned to the proxy shape //! A collection of Rprims to prepare render data for specified reprs std::unique_ptr _defaultCollection; - //! A collection of Rprims to update selection highlight - std::unique_ptr _selectionHighlightCollection; - //! A collection of Rprims being selected HdSelectionSharedPtr _selection; #if defined(WANT_UFE_BUILD) //! Observer for UFE global selection change Ufe::Observer::Ptr _ufeSelectionObserver; +#else + //! Support proxy selection highlight when UFE is not available. + MCallbackId _mayaSelectionCallbackId{ 0 }; #endif #if defined(MAYA_ENABLE_UPDATE_FOR_SELECTION) diff --git a/lib/render/vp2RenderDelegate/render_delegate.cpp b/lib/render/vp2RenderDelegate/render_delegate.cpp index a31be5ac88..876407de7d 100644 --- a/lib/render/vp2RenderDelegate/render_delegate.cpp +++ b/lib/render/vp2RenderDelegate/render_delegate.cpp @@ -86,11 +86,14 @@ namespace /*! \brief Color-indexed shader map. */ - using MShaderMap = std::unordered_map< - MColor, - MHWRender::MShaderInstance*, - MColorHash - >; + struct MShaderMap + { + //! Shader registry + std::unordered_map _map; + + //! Synchronization used to protect concurrent read from serial writes + tbb::spin_rw_mutex _mutex; + }; /*! \brief Shader cache. */ @@ -115,14 +118,6 @@ namespace TF_VERIFY(_fallbackCPVShader); - _3dSolidShader = shaderMgr->getStockShader( - MHWRender::MShaderManager::k3dSolidShader); - - if (TF_VERIFY(_3dSolidShader)) { - constexpr float color[] = { 0.056f, 1.0f, 0.366f, 1.0f }; - _3dSolidShader->setParameter(_solidColorParameterName, color); - } - _3dFatPointShader = shaderMgr->getStockShader( MHWRender::MShaderManager::k3dFatPointShader); @@ -143,18 +138,53 @@ namespace return _fallbackCPVShader; } - /*! \brief Returns a 3d green shader that can be used for selection highlight. - */ - MHWRender::MShaderInstance* Get3dSolidShader() const { - return _3dSolidShader; - } - /*! \brief Returns a white 3d fat point shader. */ MHWRender::MShaderInstance* Get3dFatPointShader() const { return _3dFatPointShader; } + /*! \brief Returns a 3d green shader that can be used for selection highlight. + */ + MHWRender::MShaderInstance* Get3dSolidShader(const MColor& color) + { + // Look for it first with reader lock + tbb::spin_rw_mutex::scoped_lock lock(_3dSolidShaders._mutex, false/*write*/); + auto it = _3dSolidShaders._map.find(color); + if (it != _3dSolidShaders._map.end()) { + return it->second; + } + + // Upgrade to writer lock. + lock.upgrade_to_writer(); + + // Double check that it wasn't inserted by another thread + it = _3dSolidShaders._map.find(color); + if (it != _3dSolidShaders._map.end()) { + return it->second; + } + + MHWRender::MShaderInstance* shader = nullptr; + + MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer(); + const MHWRender::MShaderManager* shaderMgr = + renderer ? renderer->getShaderManager() : nullptr; + if (TF_VERIFY(shaderMgr)) { + shader = shaderMgr->getStockShader( + MHWRender::MShaderManager::k3dSolidShader); + + if (TF_VERIFY(shader)) { + const float solidColor[] = { color.r, color.g, color.b, color.a }; + shader->setParameter(_solidColorParameterName, solidColor); + + // Insert instance we just created + _3dSolidShaders._map[color] = shader; + } + } + + return shader; + } + /*! \brief Returns a fallback shader instance when no material is bound. This method is keeping registry of all fallback shaders generated, @@ -168,38 +198,37 @@ namespace MHWRender::MShaderInstance* GetFallbackShader(const MColor& color) { // Look for it first with reader lock - { - tbb::reader_writer_lock::scoped_lock_read lockToRead(_fallbackShaderMutex); + tbb::spin_rw_mutex::scoped_lock lock(_fallbackShaders._mutex, false/*write*/); + auto it = _fallbackShaders._map.find(color); + if (it != _fallbackShaders._map.end()) { + return it->second; + } - auto it = _fallbackShaders.find(color); - if (it != _fallbackShaders.end()) { - return it->second; - } + // Upgrade to writer lock. + lock.upgrade_to_writer(); + + // Double check that it wasn't inserted by another thread + it = _fallbackShaders._map.find(color); + if (it != _fallbackShaders._map.end()) { + return it->second; } + MHWRender::MShaderInstance* shader = nullptr; + MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer(); const MHWRender::MShaderManager* shaderMgr = renderer ? renderer->getShaderManager() : nullptr; - if (!TF_VERIFY(shaderMgr)) - return nullptr; - - MHWRender::MShaderInstance* shader = shaderMgr->getFragmentShader( - _fallbackShaderName, _structOutputName, true); + if (TF_VERIFY(shaderMgr)) { + shader = shaderMgr->getFragmentShader(_fallbackShaderName, + _structOutputName, true); - if (TF_VERIFY(shader)) { - float diffuseColor[] = { color.r, color.g, color.b, color.a }; - shader->setParameter(_diffuseColorParameterName, diffuseColor); + if (TF_VERIFY(shader)) { + float diffuseColor[] = { color.r, color.g, color.b, color.a }; + shader->setParameter(_diffuseColorParameterName, diffuseColor); - tbb::reader_writer_lock::scoped_lock lockToWrite(_fallbackShaderMutex); - - // Double check that it wasn't inserted by another thread - auto it = _fallbackShaders.find(color); - if (it != _fallbackShaders.end()) { - return it->second; + // Insert instance we just created + _fallbackShaders._map[color] = shader; } - - // Insert instance we just created - _fallbackShaders[color] = shader; } return shader; @@ -208,11 +237,10 @@ namespace private: bool _isInitialized { false }; //!< Whether the shader cache is initialized - tbb::reader_writer_lock _fallbackShaderMutex; //!< Synchronization used to protect concurrent read from serial writes MShaderMap _fallbackShaders; //!< Shader registry used by fallback shaders + MShaderMap _3dSolidShaders; //!< Shader registry used by fallback shaders MHWRender::MShaderInstance* _fallbackCPVShader { nullptr }; //!< Fallback shader with CPV support - MHWRender::MShaderInstance* _3dSolidShader { nullptr }; //!< 3d shader for solid color MHWRender::MShaderInstance* _3dFatPointShader { nullptr }; //!< 3d shader for points }; @@ -603,7 +631,8 @@ MString HdVP2RenderDelegate::GetLocalNodeName(const MString& name) const { \return A new or existing copy of shader instance with given color parameter set */ -MHWRender::MShaderInstance* HdVP2RenderDelegate::GetFallbackShader(MColor color) const +MHWRender::MShaderInstance* HdVP2RenderDelegate::GetFallbackShader( + const MColor& color) const { return sShaderCache.GetFallbackShader(color); } @@ -617,9 +646,10 @@ MHWRender::MShaderInstance* HdVP2RenderDelegate::GetFallbackCPVShader() const /*! \brief Returns a 3d green shader that can be used for selection highlight. */ -MHWRender::MShaderInstance* HdVP2RenderDelegate::Get3dSolidShader() const +MHWRender::MShaderInstance* HdVP2RenderDelegate::Get3dSolidShader( + const MColor& color) const { - return sShaderCache.Get3dSolidShader(); + return sShaderCache.Get3dSolidShader(color); } /*! \brief Returns a white 3d fat point shader. diff --git a/lib/render/vp2RenderDelegate/render_delegate.h b/lib/render/vp2RenderDelegate/render_delegate.h index 73479518cf..9258848bf7 100644 --- a/lib/render/vp2RenderDelegate/render_delegate.h +++ b/lib/render/vp2RenderDelegate/render_delegate.h @@ -105,9 +105,9 @@ class HdVP2RenderDelegate final : public HdRenderDelegate { MString GetLocalNodeName(const MString& name) const; - MHWRender::MShaderInstance* GetFallbackShader(MColor color=MColor(0.18f,0.18f,0.18f,1.0f)) const; + MHWRender::MShaderInstance* GetFallbackShader(const MColor& color) const; MHWRender::MShaderInstance* GetFallbackCPVShader() const; - MHWRender::MShaderInstance* Get3dSolidShader() const; + MHWRender::MShaderInstance* Get3dSolidShader(const MColor& color) const; MHWRender::MShaderInstance* Get3dFatPointShader() const; const MHWRender::MSamplerState* GetSamplerState( From 961b57b572eebc2f26d1bd61410497b25ef36f94 Mon Sep 17 00:00:00 2001 From: Huidong Chen Date: Tue, 12 Nov 2019 11:17:52 -0500 Subject: [PATCH 2/5] MAYA-99443 Bounding box display for USD --- lib/CMakeLists.txt | 2 + lib/render/vp2RenderDelegate/bboxGeom.cpp | 78 ++++++++++++ lib/render/vp2RenderDelegate/bboxGeom.h | 60 +++++++++ lib/render/vp2RenderDelegate/mesh.cpp | 116 +++++++++++++++--- lib/render/vp2RenderDelegate/mesh.h | 1 + .../vp2RenderDelegate/proxyRenderDelegate.cpp | 80 ++++++++---- .../vp2RenderDelegate/render_delegate.cpp | 32 +++-- .../vp2RenderDelegate/render_delegate.h | 15 ++- lib/render/vp2RenderDelegate/tokens.cpp | 23 ++++ lib/render/vp2RenderDelegate/tokens.h | 33 +++++ 10 files changed, 381 insertions(+), 59 deletions(-) create mode 100644 lib/render/vp2RenderDelegate/bboxGeom.cpp create mode 100644 lib/render/vp2RenderDelegate/bboxGeom.h create mode 100644 lib/render/vp2RenderDelegate/tokens.cpp create mode 100644 lib/render/vp2RenderDelegate/tokens.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index df06d2a67f..c4684c626d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -59,6 +59,7 @@ list(APPEND mayaUsd_src listeners/notice.cpp listeners/proxyShapeNotice.cpp # + render/vp2RenderDelegate/bboxGeom.cpp render/vp2RenderDelegate/debugCodes.cpp render/vp2RenderDelegate/render_param.cpp render/vp2RenderDelegate/instancer.cpp @@ -68,6 +69,7 @@ list(APPEND mayaUsd_src render/vp2RenderDelegate/proxyRenderDelegate.cpp render/vp2RenderDelegate/render_delegate.cpp render/vp2RenderDelegate/sampler.cpp + render/vp2RenderDelegate/tokens.cpp # render/vp2ShaderFragments/shaderFragments.cpp # diff --git a/lib/render/vp2RenderDelegate/bboxGeom.cpp b/lib/render/vp2RenderDelegate/bboxGeom.cpp new file mode 100644 index 0000000000..a6c169707a --- /dev/null +++ b/lib/render/vp2RenderDelegate/bboxGeom.cpp @@ -0,0 +1,78 @@ +// +// Copyright 2019 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 "bboxGeom.h" + +#include "pxr/base/arch/threads.h" +#include "pxr/base/tf/diagnostic.h" + +PXR_NAMESPACE_OPEN_SCOPE + +//! Constructor. Call from main thread only. +HdVP2BBoxGeom::HdVP2BBoxGeom() +: _range(GfVec3d(-0.5, -0.5, -0.5), GfVec3d(0.5, 0.5, 0.5)) +{ + // MVertexBuffer::commit() & MIndexBuffer::commit() can work only when being + // called from main thread. + TF_VERIFY(ArchIsMainThread(), "Creating HdVP2BBoxGeom from worker threads"); + + const MHWRender::MVertexBufferDescriptor vbDesc( + "", MGeometry::kPosition, MGeometry::kFloat, 3); + + _positionBuffer.reset(new MHWRender::MVertexBuffer(vbDesc)); + + if (void* buffer = _positionBuffer->acquire(8, true)) { + constexpr float vertexData[] = { + -0.5f, -0.5f, -0.5f, // vtx 0 + -0.5f, -0.5f, 0.5f, // vtx 1 + -0.5f, 0.5f, -0.5f, // vtx 2 + -0.5f, 0.5f, 0.5f, // vtx 3 + 0.5f, -0.5f, -0.5f, // vtx 4 + 0.5f, -0.5f, 0.5f, // vtx 5 + 0.5f, 0.5f, -0.5f, // vtx 6 + 0.5f, 0.5f, 0.5f // vtx 7 + }; + + memcpy(buffer, vertexData, sizeof(vertexData)); + + _positionBuffer->commit(buffer); + } + + _indexBuffer.reset(new MHWRender::MIndexBuffer(MGeometry::kUnsignedInt32)); + + if (void* buffer = _indexBuffer->acquire(24, true)) { + constexpr unsigned int indexData[] = { + 0, 4, // edge 0 + 1, 5, // edge 1 + 2, 6, // edge 2 + 3, 7, // edge 3 + 0, 2, // edge 4 + 1, 3, // edge 5 + 4, 6, // edge 6 + 5, 7, // edge 7 + 0, 1, // edge 8 + 2, 3, // edge 9 + 4, 5, // edge 10 + 6, 7 // edge 11 + }; + + memcpy(buffer, indexData, sizeof(indexData)); + + _indexBuffer->commit(buffer); + } +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/bboxGeom.h b/lib/render/vp2RenderDelegate/bboxGeom.h new file mode 100644 index 0000000000..c7fa5e8622 --- /dev/null +++ b/lib/render/vp2RenderDelegate/bboxGeom.h @@ -0,0 +1,60 @@ +// +// Copyright 2019 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 HD_VP2_BBOX_GEOM +#define HD_VP2_BBOX_GEOM + +#include "maya/MHWGeometry.h" + +#include "pxr/pxr.h" +#include "pxr/base/gf/range3d.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +/*! \brief Geometry used for bounding box display in VP2. + \class HdVP2BBoxGeom + + The class defines a unit wire cube centered at origin. It can be used to + provide shared geometry for all Rprims to display bounding box in VP2. + The class can only be instantiated from main thread. +*/ +class HdVP2BBoxGeom final +{ +public: + HdVP2BBoxGeom(); + ~HdVP2BBoxGeom() = default; + + const MHWRender::MVertexBuffer* GetPositionBuffer() const { + return _positionBuffer.get(); + } + + const MHWRender::MIndexBuffer* GetIndexBuffer() const { + return _indexBuffer.get(); + } + + const GfRange3d& GetRange() const { return _range; } + +private: + std::unique_ptr _positionBuffer; //!< Position buffer of the geometry + std::unique_ptr _indexBuffer; //!< Index buffer of the geometry + GfRange3d _range; //!< Range of the geometry +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // HD_VP2_BBOX_GEOM diff --git a/lib/render/vp2RenderDelegate/mesh.cpp b/lib/render/vp2RenderDelegate/mesh.cpp index 629745d53a..f5b4080900 100644 --- a/lib/render/vp2RenderDelegate/mesh.cpp +++ b/lib/render/vp2RenderDelegate/mesh.cpp @@ -15,19 +15,20 @@ // #include "mesh.h" +#include "bboxGeom.h" #include "debugCodes.h" #include "draw_item.h" #include "material.h" #include "instancer.h" #include "proxyRenderDelegate.h" #include "render_delegate.h" +#include "tokens.h" #include "pxr/base/gf/matrix4d.h" #include "pxr/imaging/hd/sceneDelegate.h" #include "pxr/imaging/hd/meshUtil.h" #include "pxr/imaging/hd/smoothNormals.h" #include "pxr/imaging/hd/vertexAdjacency.h" -#include "pxr/imaging/hdx/tokens.h" #include #include @@ -609,7 +610,7 @@ void HdVP2Mesh::_InitRepr(const TfToken& reprToken, HdDirtyBits* dirtyBits) { // We don't create a repr for the selection token because it serves for // selection state update only. Mark DirtySelection bit that will be // automatically propagated to all draw items of the rprim. - if (reprToken == HdxTokens->selection) { + if (reprToken == HdVP2ReprTokens->selection) { const HdVP2SelectionStatus selectionState = param->GetDrawScene().GetPrimSelectionStatus(GetId()); if (_selectionState != selectionState) { @@ -672,12 +673,16 @@ void HdVP2Mesh::_InitRepr(const TfToken& reprToken, HdDirtyBits* dirtyBits) { renderItem = _CreateSelectionHighlightRenderItem(renderItemName); drawItem->SetUsage(HdVP2DrawItem::kSelectionHighlight); } - // The wireframe item is used for both regular drawing and - // selection highlight. - else { + // The item is used for wireframe display and selection highlight. + else if (reprToken == HdReprTokens->wire) { renderItem = _CreateWireframeRenderItem(renderItemName); drawItem->AddUsage(HdVP2DrawItem::kSelectionHighlight); } + // The item is used for bbox display and selection highlight. + else if (reprToken == HdVP2ReprTokens->bbox) { + renderItem = _CreateBoundingBoxRenderItem(renderItemName); + drawItem->AddUsage(HdVP2DrawItem::kSelectionHighlight); + } break; case HdMeshGeomStylePoints: renderItem = _CreatePointsRenderItem(renderItemName); @@ -726,7 +731,7 @@ void HdVP2Mesh::_UpdateRepr(HdSceneDelegate *sceneDelegate, const TfToken& reprT { // We don't have a repr for the selection token because it serves as a tag // for selection update only. - if (reprToken == HdxTokens->selection) { + if (reprToken == HdVP2ReprTokens->selection) { return; } @@ -775,8 +780,11 @@ void HdVP2Mesh::_UpdateDrawItem( bool requireSmoothNormals, bool requireFlatNormals) { - CommitState stateToCommit(*drawItem); - HdVP2DrawItem::RenderItemData& drawItemData = stateToCommit._drawItemData; + const MHWRender::MRenderItem* renderItem = drawItem->GetRenderItem(); + if (ARCH_UNLIKELY(!renderItem)) { + return; + } + const HdDirtyBits itemDirtyBits = drawItem->GetDirtyBits(); // We don't need to update the dedicated selection highlight item when there @@ -788,6 +796,9 @@ void HdVP2Mesh::_UpdateDrawItem( return; } + CommitState stateToCommit(*drawItem); + HdVP2DrawItem::RenderItemData& drawItemData = stateToCommit._drawItemData; + const SdfPath& id = GetId(); const HdMeshReprDesc &desc = drawItem->GetReprDesc(); @@ -804,11 +815,19 @@ void HdVP2Mesh::_UpdateDrawItem( const size_t numVertices = requiresUnsharedVertices ? topology.GetFaceVertexIndices().size() : topology.GetNumPoints(); + // The bounding box item uses a globally-shared geometry data therefore it + // doesn't need to extract index data from topology. Points use non-indexed + // draw. + const bool isBBoxItem = + (renderItem->drawMode() == MHWRender::MGeometry::kBoundingBox); + const bool isPointSnappingItem = + (renderItem->primitive() == MHWRender::MGeometry::kPoints); + const bool requiresIndexUpdate = !isBBoxItem && !isPointSnappingItem; + // Prepare index buffer. - if ((itemDirtyBits & HdChangeTracker::DirtyTopology) != 0) { + if (requiresIndexUpdate && (itemDirtyBits & HdChangeTracker::DirtyTopology)) { const HdMeshTopology* topologyToUse = unsharedTopology ? unsharedTopology : &topology; - // Index data is not required for points repr. if (desc.geomStyle == HdMeshGeomStyleHull) { HdMeshUtil meshUtil(topologyToUse, id); VtVec3iArray trianglesFaceVertexIndices; @@ -1116,21 +1135,26 @@ void HdVP2Mesh::_UpdateDrawItem( } } - // The Maya API to update bounding box of a render item is expensive, so we - // update it only when it is expanded. + // Local bounds + const GfRange3d& range = _sharedData.bounds.GetRange(); + + // Bounds are updated through MPxSubSceneOverride::setGeometryForRenderItem() + // which is expensive, so it is updated only when it gets expanded in order + // to reduce calling frequence. if (itemDirtyBits & HdChangeTracker::DirtyExtent) { - const GfRange3d& range = _sharedData.bounds.GetRange(); + const GfRange3d& rangeToUse = isBBoxItem ? + _delegate->GetSharedBBoxGeom().GetRange() : range; bool boundingBoxExpanded = false; - const GfVec3d& min = range.GetMin(); + const GfVec3d& min = rangeToUse.GetMin(); const MPoint pntMin(min[0], min[1], min[2]); if (!drawItemData._boundingBox.contains(pntMin)) { drawItemData._boundingBox.expand(pntMin); boundingBoxExpanded = true; } - const GfVec3d& max = range.GetMax(); + const GfVec3d& max = rangeToUse.GetMax(); const MPoint pntMax(max[0], max[1], max[2]); if (!drawItemData._boundingBox.contains(pntMax)) { drawItemData._boundingBox.expand(pntMax); @@ -1142,10 +1166,32 @@ void HdVP2Mesh::_UpdateDrawItem( } } + // Local-to-world transformation MMatrix& worldMatrix = drawItemData._worldMatrix; _sharedData.bounds.GetMatrix().Get(worldMatrix.matrix); - if (itemDirtyBits & HdChangeTracker::DirtyTransform) { + // The bounding box draw item uses a globally-shared unit wire cube as the + // geometry and transfers scale and offset of the bounds to world matrix. + if (isBBoxItem) { + if (itemDirtyBits & + (HdChangeTracker::DirtyExtent | HdChangeTracker::DirtyTransform)) { + if (!range.IsEmpty()) { + const GfVec3d midpoint = range.GetMidpoint(); + const GfVec3d size = range.GetSize(); + + MTransformationMatrix transformation; + transformation.setScale(size.data(), MSpace::kTransform); + transformation.setTranslation(midpoint.data(), MSpace::kTransform); + worldMatrix = transformation.asMatrix() * worldMatrix; + stateToCommit._worldMatrix = &drawItemData._worldMatrix; + } + else { + TF_DEBUG(HDVP2_DEBUG_MESH).Msg("Hydra prim '%s' has empty bounds\n", + _rprimId.asChar()); + } + } + } + else if (itemDirtyBits & HdChangeTracker::DirtyTransform) { stateToCommit._worldMatrix = &drawItemData._worldMatrix; } @@ -1253,11 +1299,20 @@ void HdVP2Mesh::_UpdateDrawItem( // Reset dirty bits because we've prepared commit state for this draw item. drawItem->ResetDirtyBits(); - // Capture class member for lambda - MHWRender::MVertexBuffer* const positionsBuffer = _meshSharedData._positionsBuffer.get(); + // Capture the valid position buffer and index buffer + MHWRender::MVertexBuffer* positionsBuffer = _meshSharedData._positionsBuffer.get(); + MHWRender::MIndexBuffer* indexBuffer = drawItemData._indexBuffer.get(); + + if (isBBoxItem) { + const HdVP2BBoxGeom& sharedBBoxGeom = _delegate->GetSharedBBoxGeom(); + positionsBuffer = const_cast( + sharedBBoxGeom.GetPositionBuffer()); + indexBuffer = const_cast( + sharedBBoxGeom.GetIndexBuffer()); + } _delegate->GetVP2ResourceRegistry().EnqueueCommit( - [drawItem, stateToCommit, param, positionsBuffer]() + [drawItem, stateToCommit, param, positionsBuffer, indexBuffer]() { MHWRender::MRenderItem* renderItem = drawItem->GetRenderItem(); if (ARCH_UNLIKELY(!renderItem)) @@ -1270,7 +1325,6 @@ void HdVP2Mesh::_UpdateDrawItem( MHWRender::MVertexBuffer* colorBuffer = drawItemData._colorBuffer.get(); MHWRender::MVertexBuffer* normalsBuffer = drawItemData._normalsBuffer.get(); - MHWRender::MIndexBuffer* indexBuffer = drawItemData._indexBuffer.get(); const HdVP2DrawItem::PrimvarBufferMap& primvarBuffers = drawItemData._primvarBuffers; @@ -1477,6 +1531,28 @@ MHWRender::MRenderItem* HdVP2Mesh::_CreateWireframeRenderItem( return renderItem; } +/*! \brief Create render item for bbox repr. +*/ +MHWRender::MRenderItem* HdVP2Mesh::_CreateBoundingBoxRenderItem( + const MString& name) const +{ + MHWRender::MRenderItem* const renderItem = MHWRender::MRenderItem::Create( + name, + MHWRender::MRenderItem::DecorationItem, + MHWRender::MGeometry::kLines + ); + + renderItem->setDrawMode(MHWRender::MGeometry::kBoundingBox); + renderItem->castsShadows(false); + renderItem->receivesShadows(false); + renderItem->setShader(_delegate->Get3dSolidShader(kOpaqueBlue)); + renderItem->setSelectionMask(MSelectionMask::kSelectMeshes); + + setWantConsolidation(*renderItem, true); + + return renderItem; +} + /*! \brief Create render item for smoothHull repr. */ MHWRender::MRenderItem* HdVP2Mesh::_CreateSmoothHullRenderItem(const MString& name) const diff --git a/lib/render/vp2RenderDelegate/mesh.h b/lib/render/vp2RenderDelegate/mesh.h index ed5a5ad7e9..a7ccc1658e 100644 --- a/lib/render/vp2RenderDelegate/mesh.h +++ b/lib/render/vp2RenderDelegate/mesh.h @@ -110,6 +110,7 @@ class HdVP2Mesh final : public HdMesh { MHWRender::MRenderItem* _CreateSmoothHullRenderItem(const MString& name) const; MHWRender::MRenderItem* _CreateWireframeRenderItem(const MString& name) const; MHWRender::MRenderItem* _CreatePointsRenderItem(const MString& name) const; + MHWRender::MRenderItem* _CreateBoundingBoxRenderItem(const MString& name) const; //! Custom dirty bits used by this mesh enum DirtyBits : HdDirtyBits { diff --git a/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp b/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp index ad713c0ba5..53f2ddf5aa 100644 --- a/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp +++ b/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp @@ -16,13 +16,13 @@ #include "proxyRenderDelegate.h" #include "render_delegate.h" +#include "tokens.h" #include "pxr/base/tf/diagnostic.h" #include "pxr/usdImaging/usdImaging/delegate.h" #include "pxr/imaging/hdx/renderTask.h" #include "pxr/imaging/hdx/selectionTracker.h" #include "pxr/imaging/hdx/taskController.h" -#include "pxr/imaging/hdx/tokens.h" #include "pxr/imaging/hd/enums.h" #include "pxr/imaging/hd/mesh.h" #include "pxr/imaging/hd/repr.h" @@ -57,9 +57,15 @@ namespace //! Representation selector for wireframe viewport mode const HdReprSelector kWireReprSelector(TfToken(), HdReprTokens->wire); + //! Representation selector for bounding box viewport mode + const HdReprSelector kBBoxReprSelector(TfToken(), HdVP2ReprTokens->bbox); + //! Representation selector for point snapping const HdReprSelector kPointsReprSelector(TfToken(), TfToken(), HdReprTokens->points); + //! Representation selector for selection update + const HdReprSelector kSelectionReprSelector(HdVP2ReprTokens->selection); + MGlobal::ListAdjustment GetListAdjustment() { // Keyboard modifiers can be queried from QApplication::keyboardModifiers() @@ -93,23 +99,30 @@ namespace //! \brief Configure repr descriptions void _ConfigureReprs() { - // One desc for shaded display, the other desc for selection highlight. - HdMesh::ConfigureRepr(HdReprTokens->smoothHull, - HdMeshReprDesc(HdMeshGeomStyleHull, - HdCullStyleDontCare, - HdMeshReprDescTokens->surfaceShader, - /*flatShadingEnabled=*/false, - /*blendWireframeColor=*/false), - HdMeshReprDesc(HdMeshGeomStyleHullEdgeOnly, - HdCullStyleDontCare, - HdMeshReprDescTokens->surfaceShader, - /*flatShadingEnabled=*/false, - /*blendWireframeColor=*/false) + const HdMeshReprDesc reprDescHull( + HdMeshGeomStyleHull, + HdCullStyleDontCare, + HdMeshReprDescTokens->surfaceShader, + /*flatShadingEnabled=*/false, + /*blendWireframeColor=*/false); + + const HdMeshReprDesc reprDescEdge( + HdMeshGeomStyleHullEdgeOnly, + HdCullStyleDontCare, + HdMeshReprDescTokens->surfaceShader, + /*flatShadingEnabled=*/false, + /*blendWireframeColor=*/false ); + // Hull desc for shaded display, edge desc for selection highlight. + HdMesh::ConfigureRepr(HdReprTokens->smoothHull, reprDescHull, reprDescEdge); + + // Edge desc for bbox display. + HdMesh::ConfigureRepr(HdVP2ReprTokens->bbox, reprDescEdge); + // Special token for selection update and no need to create repr. Adding // the empty desc to remove Hydra warning. - HdMesh::ConfigureRepr(HdxTokens->selection, HdMeshReprDesc()); + HdMesh::ConfigureRepr(HdVP2ReprTokens->selection, HdMeshReprDesc()); } #if defined(WANT_UFE_BUILD) @@ -359,20 +372,36 @@ void ProxyRenderDelegate::_Execute(const MHWRender::MFrameContext& frameContext) const unsigned int displayStyle = frameContext.getDisplayStyle(); - if (displayStyle & MHWRender::MFrameContext::kGouraudShaded) { - if (!reprSelector.Contains(HdReprTokens->smoothHull)) { - reprSelector = reprSelector.CompositeOver(kSmoothHullReprSelector); - } + // Query the wireframe color assigned to proxy shape. + if (displayStyle & ( + MHWRender::MFrameContext::kBoundingBox | + MHWRender::MFrameContext::kWireFrame)) + { + MDagPath proxyPath; + MDagPath::getAPathTo(_mObject, proxyPath); + _wireframeColor = MHWRender::MGeometryUtilities::wireframeColor(proxyPath); } - if (displayStyle & MHWRender::MFrameContext::kWireFrame) { - if (!reprSelector.Contains(HdReprTokens->wire)) { - reprSelector = reprSelector.CompositeOver(kWireReprSelector); + // Update repr selector based on display style of the current viewport + if (displayStyle & MHWRender::MFrameContext::kBoundingBox) { + if (!reprSelector.Contains(HdVP2ReprTokens->bbox)) { + reprSelector = reprSelector.CompositeOver(kBBoxReprSelector); + } + } + else { + // To support Wireframe on Shaded mode, the two displayStyle checks + // should not be mutually excluded. + if (displayStyle & MHWRender::MFrameContext::kGouraudShaded) { + if (!reprSelector.Contains(HdReprTokens->smoothHull)) { + reprSelector = reprSelector.CompositeOver(kSmoothHullReprSelector); + } } - MDagPath proxyDagPath; - MDagPath::getAPathTo(_mObject, proxyDagPath); - _wireframeColor = MHWRender::MGeometryUtilities::wireframeColor(proxyDagPath); + if (displayStyle & MHWRender::MFrameContext::kWireFrame) { + if (!reprSelector.Contains(HdReprTokens->wire)) { + reprSelector = reprSelector.CompositeOver(kWireReprSelector); + } + } } } @@ -576,8 +605,7 @@ void ProxyRenderDelegate::_UpdateSelectionStates() } if (!rootPaths.empty()) { - HdRprimCollection collection(HdTokens->geometry, - HdReprSelector(HdxTokens->selection)); + HdRprimCollection collection(HdTokens->geometry, kSelectionReprSelector); collection.SetRootPaths(rootPaths); _taskController->SetCollection(collection); _engine.Execute(_renderIndex, &_dummyTasks); diff --git a/lib/render/vp2RenderDelegate/render_delegate.cpp b/lib/render/vp2RenderDelegate/render_delegate.cpp index 876407de7d..423168e2a8 100644 --- a/lib/render/vp2RenderDelegate/render_delegate.cpp +++ b/lib/render/vp2RenderDelegate/render_delegate.cpp @@ -16,6 +16,7 @@ #include "render_delegate.h" +#include "bboxGeom.h" #include "material.h" #include "mesh.h" @@ -310,6 +311,17 @@ namespace MSamplerStateCache sSamplerStates; //!< Sampler state cache tbb::spin_rw_mutex sSamplerRWMutex; //!< Synchronization used to protect concurrent read from serial writes + /*! \brief Shared bounding box geometry + + The shared bounding box geometry will be cached until the end of a Maya + session and used by all Rprims to display bounding boxes in VP2. + + We use heap allocation without smart pointer to avoid the destructor to + be called after VP2 has destructed graphics device context when quiting + maya, otherwise it would crash. + */ + const HdVP2BBoxGeom* const sSharedBBoxGeom = new HdVP2BBoxGeom; + } // namespace const int HdVP2RenderDelegate::sProfilerCategory = MProfiler::addCategory( @@ -320,8 +332,8 @@ const int HdVP2RenderDelegate::sProfilerCategory = MProfiler::addCategory( #endif ); -std::mutex HdVP2RenderDelegate::_mutexResourceRegistry; -std::atomic_int HdVP2RenderDelegate::_counterResourceRegistry; +std::mutex HdVP2RenderDelegate::_renderDelegateMutex; +std::atomic_int HdVP2RenderDelegate::_renderDelegateCounter; HdResourceRegistrySharedPtr HdVP2RenderDelegate::_resourceRegistry; /*! \brief Constructor. @@ -329,8 +341,8 @@ HdResourceRegistrySharedPtr HdVP2RenderDelegate::_resourceRegistry; HdVP2RenderDelegate::HdVP2RenderDelegate(ProxyRenderDelegate& drawScene) { _id = SdfPath(TfToken(TfStringPrintf("/HdVP2RenderDelegate_%p", this))); - std::lock_guard guard(_mutexResourceRegistry); - if (_counterResourceRegistry.fetch_add(1) == 0) { + std::lock_guard guard(_renderDelegateMutex); + if (_renderDelegateCounter.fetch_add(1) == 0) { _resourceRegistry.reset(new HdResourceRegistry()); } @@ -343,9 +355,8 @@ HdVP2RenderDelegate::HdVP2RenderDelegate(ProxyRenderDelegate& drawScene) { /*! \brief Destructor. */ HdVP2RenderDelegate::~HdVP2RenderDelegate() { - std::lock_guard guard(_mutexResourceRegistry); - - if (_counterResourceRegistry.fetch_sub(1) == 1) { + std::lock_guard guard(_renderDelegateMutex); + if (_renderDelegateCounter.fetch_sub(1) == 1) { _resourceRegistry.reset(); } } @@ -688,4 +699,11 @@ const MHWRender::MSamplerState* HdVP2RenderDelegate::GetSamplerState( return samplerState; } +/*! \brief Returns the shared bbox geometry. +*/ +const HdVP2BBoxGeom& HdVP2RenderDelegate::GetSharedBBoxGeom() const +{ + return *sSharedBBoxGeom; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/render_delegate.h b/lib/render/vp2RenderDelegate/render_delegate.h index 9258848bf7..0a5845fbd3 100644 --- a/lib/render/vp2RenderDelegate/render_delegate.h +++ b/lib/render/vp2RenderDelegate/render_delegate.h @@ -32,6 +32,7 @@ PXR_NAMESPACE_OPEN_SCOPE +class HdVP2BBoxGeom; class ProxyRenderDelegate; /*! \brief VP2 render delegate @@ -113,19 +114,21 @@ class HdVP2RenderDelegate final : public HdRenderDelegate { const MHWRender::MSamplerState* GetSamplerState( const MHWRender::MSamplerStateDesc& desc) const; + const HdVP2BBoxGeom& GetSharedBBoxGeom() const; + static const int sProfilerCategory; //!< Profiler category private: HdVP2RenderDelegate(const HdVP2RenderDelegate&) = delete; HdVP2RenderDelegate& operator=(const HdVP2RenderDelegate&) = delete; - static std::mutex _mutexResourceRegistry; //!< Mutex protecting construction/destruction of resource registry - static std::atomic_int _counterResourceRegistry; //!< Number of render delegates sharing this resource registry. Last one deletes the instance. - static HdResourceRegistrySharedPtr _resourceRegistry; //!< Shared and unused by VP2 resource registry + static std::atomic_int _renderDelegateCounter; //!< Number of render delegates. First one creates shared resources and last one deletes them. + static std::mutex _renderDelegateMutex; //!< Mutex protecting construction/destruction of render delegate + static HdResourceRegistrySharedPtr _resourceRegistry; //!< Shared and unused by VP2 resource registry - std::unique_ptr _renderParam; //!< Render param used to provided access to VP2 during prim synchronization - SdfPath _id; //!< Render delegate IDs - HdVP2ResourceRegistry _resourceRegistryVP2; //!< VP2 resource registry used for enqueue and execution of commits + std::unique_ptr _renderParam; //!< Render param used to provided access to VP2 during prim synchronization + SdfPath _id; //!< Render delegate IDs + HdVP2ResourceRegistry _resourceRegistryVP2; //!< VP2 resource registry used for enqueue and execution of commits }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/tokens.cpp b/lib/render/vp2RenderDelegate/tokens.cpp new file mode 100644 index 0000000000..1b7f485be6 --- /dev/null +++ b/lib/render/vp2RenderDelegate/tokens.cpp @@ -0,0 +1,23 @@ +// +// Copyright 2019 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 "tokens.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PUBLIC_TOKENS(HdVP2ReprTokens, HDVP2_REPR_TOKENS); + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/render/vp2RenderDelegate/tokens.h b/lib/render/vp2RenderDelegate/tokens.h new file mode 100644 index 0000000000..59780b8f7b --- /dev/null +++ b/lib/render/vp2RenderDelegate/tokens.h @@ -0,0 +1,33 @@ +// +// Copyright 2019 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 HD_VP2_TOKENS_H +#define HD_VP2_TOKENS_H + +#include "pxr/pxr.h" +#include "pxr/base/tf/staticTokens.h" + +PXR_NAMESPACE_OPEN_SCOPE + +#define HDVP2_REPR_TOKENS \ + (bbox) \ + (selection) + +TF_DECLARE_PUBLIC_TOKENS(HdVP2ReprTokens, , HDVP2_REPR_TOKENS); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // HD_VP2_TOKENS_H From 0e23a73d62a7fab9a7d7057ae133faf0c20b853a Mon Sep 17 00:00:00 2001 From: Huidong Chen Date: Thu, 7 Nov 2019 11:19:05 -0500 Subject: [PATCH 3/5] MAYA-99904 respect proxy transformation and visibility (#185) --- .../vp2RenderDelegate/proxyRenderDelegate.cpp | 88 +++++++++++-------- .../vp2RenderDelegate/proxyRenderDelegate.h | 8 +- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp b/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp index 53f2ddf5aa..60397bd342 100644 --- a/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp +++ b/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp @@ -177,8 +177,12 @@ MHWRender::MPxSubSceneOverride* ProxyRenderDelegate::Creator(const MObject& obj) //! \brief Constructor ProxyRenderDelegate::ProxyRenderDelegate(const MObject& obj) - : MHWRender::MPxSubSceneOverride(obj) - , _mObject(obj) { +: MHWRender::MPxSubSceneOverride(obj) +{ + MDagPath::getAPathTo(obj, _proxyDagPath); + + const MFnDependencyNode fnDepNode(obj); + _proxyShape = static_cast(fnDepNode.userNode()); } //! \brief Destructor @@ -212,26 +216,17 @@ bool ProxyRenderDelegate::requiresUpdate(const MSubSceneContainer& container, co return true; } -//! \brief Return pointer to DG proxy shape node -MayaUsdProxyShapeBase* ProxyRenderDelegate::getProxyShape() const { - const MFnDependencyNode depNodeFn(_mObject); - MayaUsdProxyShapeBase* usdSubSceneShape = static_cast(depNodeFn.userNode()); - return usdSubSceneShape; -} - //! \brief One time initialization of this drawing routine void ProxyRenderDelegate::_InitRenderDelegate() { // No need to run all the checks if we got till the end if (_isInitialized()) return; - MStatus status; - MayaUsdProxyShapeBase* usdSubSceneShape = getProxyShape(); - if (!usdSubSceneShape) + if (_proxyShape == nullptr) return; if (!_usdStage) { - _usdStage = usdSubSceneShape->getUsdStage(); + _usdStage = _proxyShape->getUsdStage(); } if (!_renderDelegate) { @@ -255,7 +250,7 @@ void ProxyRenderDelegate::_InitRenderDelegate() { MProfiler::kColorD_L1, "Allocate SceneDelegate"); SdfPath delegateID = SdfPath::AbsoluteRootPath().AppendChild(TfToken(TfStringPrintf( - "Proxy_%s_%p", usdSubSceneShape->name().asChar(), usdSubSceneShape))); + "Proxy_%s_%p", _proxyShape->name().asChar(), _proxyShape))); _sceneDelegate = new UsdImagingDelegate(_renderIndex, delegateID); @@ -300,13 +295,12 @@ bool ProxyRenderDelegate::_Populate() { if (!_isInitialized()) return false; - auto* proxyShape = getProxyShape(); - if (_usdStage && (!_isPopulated || proxyShape->getExcludePrimPathsVersion() != _excludePrimPathsVersion) ) { + if (_usdStage && (!_isPopulated || _proxyShape->getExcludePrimPathsVersion() != _excludePrimPathsVersion) ) { MProfilingScope subProfilingScope(HdVP2RenderDelegate::sProfilerCategory, MProfiler::kColorD_L1, "Populate"); // It might have been already populated, clear it if so. - SdfPathVector excludePrimPaths = proxyShape->getExcludePrimPaths(); + SdfPathVector excludePrimPaths = _proxyShape->getExcludePrimPaths(); for (auto& excludePrim : excludePrimPaths) { SdfPath indexPath = _sceneDelegate->ConvertCachePathToIndexPath(excludePrim); if (_renderIndex->HasRprim(indexPath)) { @@ -317,22 +311,48 @@ bool ProxyRenderDelegate::_Populate() { _sceneDelegate->Populate(_usdStage->GetPseudoRoot(),excludePrimPaths); _isPopulated = true; - _excludePrimPathsVersion = proxyShape->getExcludePrimPathsVersion(); + _excludePrimPathsVersion = _proxyShape->getExcludePrimPathsVersion(); } return _isPopulated; } -//! \brief Synchronize USD scene delegate time with Maya's scene time. -void ProxyRenderDelegate::_UpdateTime() { +//! \brief Synchronize USD scene delegate with Maya's proxy shape. +void ProxyRenderDelegate::_UpdateSceneDelegate() +{ + if (!_proxyShape || !_sceneDelegate) { + return; + } + MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, - MProfiler::kColorC_L1, "Update Time"); + MProfiler::kColorC_L1, "UpdateSceneDelegate"); + + { + MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, + MProfiler::kColorC_L1, "SetTime"); - MayaUsdProxyShapeBase* usdSubSceneShape = getProxyShape(); - if(usdSubSceneShape && _sceneDelegate) { - UsdTimeCode timeCode = usdSubSceneShape->getTime(); + const UsdTimeCode timeCode = _proxyShape->getTime(); _sceneDelegate->SetTime(timeCode); } + + { + MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, + MProfiler::kColorC_L1, "SetRootTransform"); + + // Rprim sync will be triggered only if root transform is changed. + const MMatrix inclusiveMatrix = _proxyDagPath.inclusiveMatrix(); + const GfMatrix4d rootTransform(inclusiveMatrix.matrix); + _sceneDelegate->SetRootTransform(rootTransform); + } + + { + MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, + MProfiler::kColorC_L1, "SetRootVisibility"); + + // Rprim sync will be triggered only if root visibility is changed. + const bool isVisible = _proxyDagPath.isVisible(); + _sceneDelegate->SetRootVisibility(isVisible); + } } //! \brief Execute Hydra engine to perform minimal VP2 draw data update based on change tracker. @@ -377,9 +397,7 @@ void ProxyRenderDelegate::_Execute(const MHWRender::MFrameContext& frameContext) MHWRender::MFrameContext::kBoundingBox | MHWRender::MFrameContext::kWireFrame)) { - MDagPath proxyPath; - MDagPath::getAPathTo(_mObject, proxyPath); - _wireframeColor = MHWRender::MGeometryUtilities::wireframeColor(proxyPath); + _wireframeColor = MHWRender::MGeometryUtilities::wireframeColor(_proxyDagPath); } // Update repr selector based on display style of the current viewport @@ -425,7 +443,7 @@ void ProxyRenderDelegate::update(MSubSceneContainer& container, const MFrameCont param->BeginUpdate(container, _sceneDelegate->GetTime()); if (_Populate()) { - _UpdateTime(); + _UpdateSceneDelegate(); _Execute(frameContext); } param->EndUpdate(); @@ -459,8 +477,7 @@ bool ProxyRenderDelegate::getInstancedSelectionPath( if (handler == nullptr) return false; - MayaUsdProxyShapeBase* proxyShape = getProxyShape(); - if (proxyShape == nullptr) + if (_proxyShape == nullptr) return false; // Extract id of the owner Rprim. A SdfPath directly created from the render @@ -484,7 +501,7 @@ bool ProxyRenderDelegate::getInstancedSelectionPath( const SdfPath usdPath(_sceneDelegate->ConvertIndexPathToCachePath(rprimId)); const Ufe::PathSegment pathSegment(usdPath.GetText(), USD_UFE_RUNTIME_ID, USD_UFE_SEPARATOR); - const Ufe::SceneItem::Ptr& si = handler->createItem(proxyShape->ufePath() + pathSegment); + const Ufe::SceneItem::Ptr& si = handler->createItem(_proxyShape->ufePath() + pathSegment); if (!si) { TF_WARN("UFE runtime is not updated for the USD stage. Please save scene and reopen."); return false; @@ -541,14 +558,13 @@ void ProxyRenderDelegate::SelectionChanged() void ProxyRenderDelegate::_FilterSelection() { #if defined(WANT_UFE_BUILD) - MayaUsdProxyShapeBase* proxyShape = getProxyShape(); - if (proxyShape == nullptr) { + if (_proxyShape == nullptr) { return; } _selection.reset(new HdSelection); - const auto proxyPath = proxyShape->ufePath(); + const auto proxyPath = _proxyShape->ufePath(); const auto globalSelection = Ufe::GlobalSelection::get(); for (const Ufe::SceneItem::Ptr& item : *globalSelection) { @@ -574,9 +590,7 @@ void ProxyRenderDelegate::_FilterSelection() */ void ProxyRenderDelegate::_UpdateSelectionStates() { - MDagPath proxyDagPath; - MDagPath::getAPathTo(_mObject, proxyDagPath); - auto status = MHWRender::MGeometryUtilities::displayStatus(proxyDagPath); + auto status = MHWRender::MGeometryUtilities::displayStatus(_proxyDagPath); const bool wasProxySelected = _isProxySelected; _isProxySelected = ((status == MHWRender::kHilite) || (status == MHWRender::kLead)); diff --git a/lib/render/vp2RenderDelegate/proxyRenderDelegate.h b/lib/render/vp2RenderDelegate/proxyRenderDelegate.h index c02944c29e..6f8828748f 100644 --- a/lib/render/vp2RenderDelegate/proxyRenderDelegate.h +++ b/lib/render/vp2RenderDelegate/proxyRenderDelegate.h @@ -112,8 +112,9 @@ class ProxyRenderDelegate : public MHWRender::MPxSubSceneOverride const MIntersection& intersection, MDagPath& dagPath) const override; + //! \brief Return pointer to DG proxy shape node MAYAUSD_CORE_PUBLIC - MayaUsdProxyShapeBase* getProxyShape() const; + const MayaUsdProxyShapeBase* getProxyShape() const { return _proxyShape; } MAYAUSD_CORE_PUBLIC void SelectionChanged(); @@ -133,7 +134,7 @@ class ProxyRenderDelegate : public MHWRender::MPxSubSceneOverride void _InitRenderDelegate(); bool _Populate(); - void _UpdateTime(); + void _UpdateSceneDelegate(); void _Execute(const MHWRender::MFrameContext& frameContext); bool _isInitialized(); @@ -141,7 +142,8 @@ class ProxyRenderDelegate : public MHWRender::MPxSubSceneOverride void _FilterSelection(); void _UpdateSelectionStates(); - MObject _mObject; //!< Proxy shape MObject + const MayaUsdProxyShapeBase* _proxyShape{ nullptr }; //!< DG proxy shape node + MDagPath _proxyDagPath; //!< DAG path of the proxy shape (assuming no DAG instancing) // USD & Hydra Objects HdEngine _engine; //!< Hydra engine responsible for running synchronization between scene delegate and VP2RenderDelegate From 49b8a665fc44f7d162db701b20c85e658c4a78e3 Mon Sep 17 00:00:00 2001 From: Huidong Chen Date: Mon, 11 Nov 2019 10:04:02 -0500 Subject: [PATCH 4/5] MAYA-101928 Hide bbox for Rprims without authored extent (#186) --- lib/render/vp2RenderDelegate/mesh.cpp | 50 ++++++++++++++++----------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/lib/render/vp2RenderDelegate/mesh.cpp b/lib/render/vp2RenderDelegate/mesh.cpp index f5b4080900..b7072bd65d 100644 --- a/lib/render/vp2RenderDelegate/mesh.cpp +++ b/lib/render/vp2RenderDelegate/mesh.cpp @@ -790,7 +790,9 @@ void HdVP2Mesh::_UpdateDrawItem( // We don't need to update the dedicated selection highlight item when there // is no selection highlight change and the mesh is not selected. Draw item // has its own dirty bits, so update will be doner when it shows in viewport. - if (drawItem->MatchesUsage(HdVP2DrawItem::kSelectionHighlight) && + const bool isDedicatedSelectionHighlightItem = + drawItem->MatchesUsage(HdVP2DrawItem::kSelectionHighlight); + if (isDedicatedSelectionHighlightItem && ((itemDirtyBits & DirtySelectionHighlight) == 0) && (_selectionState == kUnselected)) { return; @@ -1173,22 +1175,16 @@ void HdVP2Mesh::_UpdateDrawItem( // The bounding box draw item uses a globally-shared unit wire cube as the // geometry and transfers scale and offset of the bounds to world matrix. if (isBBoxItem) { - if (itemDirtyBits & - (HdChangeTracker::DirtyExtent | HdChangeTracker::DirtyTransform)) { - if (!range.IsEmpty()) { - const GfVec3d midpoint = range.GetMidpoint(); - const GfVec3d size = range.GetSize(); - - MTransformationMatrix transformation; - transformation.setScale(size.data(), MSpace::kTransform); - transformation.setTranslation(midpoint.data(), MSpace::kTransform); - worldMatrix = transformation.asMatrix() * worldMatrix; - stateToCommit._worldMatrix = &drawItemData._worldMatrix; - } - else { - TF_DEBUG(HDVP2_DEBUG_MESH).Msg("Hydra prim '%s' has empty bounds\n", - _rprimId.asChar()); - } + if ((itemDirtyBits & (HdChangeTracker::DirtyExtent | HdChangeTracker::DirtyTransform)) && + !range.IsEmpty()) { + const GfVec3d midpoint = range.GetMidpoint(); + const GfVec3d size = range.GetSize(); + + MTransformationMatrix transformation; + transformation.setScale(size.data(), MSpace::kTransform); + transformation.setTranslation(midpoint.data(), MSpace::kTransform); + worldMatrix = transformation.asMatrix() * worldMatrix; + stateToCommit._worldMatrix = &drawItemData._worldMatrix; } } else if (itemDirtyBits & HdChangeTracker::DirtyTransform) { @@ -1208,7 +1204,7 @@ void HdVP2Mesh::_UpdateDrawItem( MMatrix instanceMatrix; - if (drawItem->MatchesUsage(HdVP2DrawItem::kSelectionHighlight)) { + if (isDedicatedSelectionHighlightItem) { if (_selectionState == kFullySelected) { stateToCommit._instanceTransforms.setLength(transforms.size()); for (size_t i = 0; i < transforms.size(); ++i) { @@ -1288,11 +1284,23 @@ void HdVP2Mesh::_UpdateDrawItem( stateToCommit._enabled = &drawItemData._enabled; } - if (drawItem->MatchesUsage(HdVP2DrawItem::kSelectionHighlight)) { + if (isDedicatedSelectionHighlightItem) { if (itemDirtyBits & DirtySelectionHighlight) { - drawItemData._enabled = + const bool enable = (_selectionState != kUnselected) && drawItem->GetVisible(); - stateToCommit._enabled = &drawItemData._enabled; + if (drawItemData._enabled != enable) { + drawItemData._enabled = enable; + stateToCommit._enabled = &drawItemData._enabled; + } + } + } + else if (isBBoxItem) { + if (itemDirtyBits & HdChangeTracker::DirtyExtent) { + const bool enable = !range.IsEmpty() && drawItem->GetVisible(); + if (drawItemData._enabled != enable) { + drawItemData._enabled = enable; + stateToCommit._enabled = &drawItemData._enabled; + } } } From 6280cea2e8e7f7515fb635d9af09af302b35412b Mon Sep 17 00:00:00 2001 From: Huidong Chen Date: Wed, 13 Nov 2019 14:57:03 -0500 Subject: [PATCH 5/5] Fix crash and hiding issue reported by BSKY. --- lib/render/vp2RenderDelegate/mesh.cpp | 13 +++++----- .../vp2RenderDelegate/proxyRenderDelegate.cpp | 22 +++++++++-------- .../vp2RenderDelegate/render_delegate.cpp | 24 +++++++++++-------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/lib/render/vp2RenderDelegate/mesh.cpp b/lib/render/vp2RenderDelegate/mesh.cpp index b7072bd65d..cc8e25769d 100644 --- a/lib/render/vp2RenderDelegate/mesh.cpp +++ b/lib/render/vp2RenderDelegate/mesh.cpp @@ -364,6 +364,13 @@ void HdVP2Mesh::Sync( HdDirtyBits* dirtyBits, const TfToken& reprToken) { + // We don't create a repr for the selection token because this token serves + // for selection state update only. Return early to reserve dirty bits so + // they can be used to sync regular reprs later. + if (reprToken == HdVP2ReprTokens->selection) { + return; + } + MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, MProfiler::kColorC_L2, _rprimId.asChar(), "HdVP2Mesh::Sync"); @@ -729,12 +736,6 @@ void HdVP2Mesh::_InitRepr(const TfToken& reprToken, HdDirtyBits* dirtyBits) { */ void HdVP2Mesh::_UpdateRepr(HdSceneDelegate *sceneDelegate, const TfToken& reprToken) { - // We don't have a repr for the selection token because it serves as a tag - // for selection update only. - if (reprToken == HdVP2ReprTokens->selection) { - return; - } - HdReprSharedPtr const &curRepr = _GetRepr(reprToken); if (!curRepr) { return; diff --git a/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp b/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp index 60397bd342..4aea5734ec 100644 --- a/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp +++ b/lib/render/vp2RenderDelegate/proxyRenderDelegate.cpp @@ -335,23 +335,25 @@ void ProxyRenderDelegate::_UpdateSceneDelegate() _sceneDelegate->SetTime(timeCode); } - { + const MMatrix inclusiveMatrix = _proxyDagPath.inclusiveMatrix(); + const GfMatrix4d transform(inclusiveMatrix.matrix); + constexpr double tolerance = 1e-9; + if (!GfIsClose(transform, _sceneDelegate->GetRootTransform(), tolerance)) { MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, MProfiler::kColorC_L1, "SetRootTransform"); - - // Rprim sync will be triggered only if root transform is changed. - const MMatrix inclusiveMatrix = _proxyDagPath.inclusiveMatrix(); - const GfMatrix4d rootTransform(inclusiveMatrix.matrix); - _sceneDelegate->SetRootTransform(rootTransform); + _sceneDelegate->SetRootTransform(transform); } - { + const bool isVisible = _proxyDagPath.isVisible(); + if (isVisible != _sceneDelegate->GetRootVisibility()) { MProfilingScope profilingScope(HdVP2RenderDelegate::sProfilerCategory, MProfiler::kColorC_L1, "SetRootVisibility"); - - // Rprim sync will be triggered only if root visibility is changed. - const bool isVisible = _proxyDagPath.isVisible(); _sceneDelegate->SetRootVisibility(isVisible); + + // Trigger selection update when a hidden proxy shape gets shown. + if (isVisible) { + SelectionChanged(); + } } } diff --git a/lib/render/vp2RenderDelegate/render_delegate.cpp b/lib/render/vp2RenderDelegate/render_delegate.cpp index 423168e2a8..cde7047f67 100644 --- a/lib/render/vp2RenderDelegate/render_delegate.cpp +++ b/lib/render/vp2RenderDelegate/render_delegate.cpp @@ -311,16 +311,7 @@ namespace MSamplerStateCache sSamplerStates; //!< Sampler state cache tbb::spin_rw_mutex sSamplerRWMutex; //!< Synchronization used to protect concurrent read from serial writes - /*! \brief Shared bounding box geometry - - The shared bounding box geometry will be cached until the end of a Maya - session and used by all Rprims to display bounding boxes in VP2. - - We use heap allocation without smart pointer to avoid the destructor to - be called after VP2 has destructed graphics device context when quiting - maya, otherwise it would crash. - */ - const HdVP2BBoxGeom* const sSharedBBoxGeom = new HdVP2BBoxGeom; + const HdVP2BBoxGeom* sSharedBBoxGeom = nullptr; //!< Shared geometry for all Rprims to display bounding box } // namespace @@ -344,6 +335,14 @@ HdVP2RenderDelegate::HdVP2RenderDelegate(ProxyRenderDelegate& drawScene) { std::lock_guard guard(_renderDelegateMutex); if (_renderDelegateCounter.fetch_add(1) == 0) { _resourceRegistry.reset(new HdResourceRegistry()); + + // HdVP2BBoxGeom can only be instantiated during the lifetime of VP2 + // renderer from main thread. HdVP2RenderDelegate is created from main + // thread currently, if we need to make its creation parallel in the + // future we should move this code out. + if (TF_VERIFY(sSharedBBoxGeom == nullptr)) { + sSharedBBoxGeom = new HdVP2BBoxGeom(); + } } _renderParam.reset(new HdVP2RenderParam(drawScene)); @@ -358,6 +357,11 @@ HdVP2RenderDelegate::~HdVP2RenderDelegate() { std::lock_guard guard(_renderDelegateMutex); if (_renderDelegateCounter.fetch_sub(1) == 1) { _resourceRegistry.reset(); + + if (TF_VERIFY(sSharedBBoxGeom)) { + delete sSharedBBoxGeom; + sSharedBBoxGeom = nullptr; + } } }