diff --git a/lib/mayaUsd/render/px_vp20/utils.cpp b/lib/mayaUsd/render/px_vp20/utils.cpp index 8a18f28bab..c90136da97 100644 --- a/lib/mayaUsd/render/px_vp20/utils.cpp +++ b/lib/mayaUsd/render/px_vp20/utils.cpp @@ -763,8 +763,8 @@ px_vp20Utils::RenderBoundingBox( bboxTransformMatrix.setScale(scales, MSpace::kTransform); return RenderWireCubes( { GfMatrix4f(bboxTransformMatrix.asMatrix().matrix) }, - color, - GfMatrix4d(worldViewMat.matrix), + color, + GfMatrix4d(worldViewMat.matrix), GfMatrix4d(projectionMat.matrix)); } @@ -875,7 +875,7 @@ void main() // Populate the shader variables. GfMatrix4f vpMatrix(worldViewMat * projectionMat); GLuint vpMatrixLoc = glGetUniformLocation(renderBoundsProgramId, "vpMatrix"); - glUniformMatrix4fv(vpMatrixLoc, 1, + glUniformMatrix4fv(vpMatrixLoc, 1, GL_TRUE, // transpose vpMatrix.data()); @@ -891,12 +891,12 @@ void main() // since we're copying these directly from GfMatrix4f, we need to // transpose() them in the shader. const GLuint cubeXformLoc = glGetAttribLocation(renderBoundsProgramId, "cubeXformT"); - glBufferData(GL_ARRAY_BUFFER, + glBufferData(GL_ARRAY_BUFFER, sizeof(GfMatrix4f) * numCubes, cubeXforms.data(), GL_DYNAMIC_DRAW); for (size_t r = 0; r < 4; r++) { GLuint loc = cubeXformLoc + r; glEnableVertexAttribArray(loc); - glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, sizeof(GfMatrix4f), + glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, sizeof(GfMatrix4f), (char*)(sizeof(float)*4*r)); glVertexAttribDivisor(loc, 1); } diff --git a/lib/mayaUsd/render/px_vp20/utils.h b/lib/mayaUsd/render/px_vp20/utils.h index c7ef3dc45b..8204c583eb 100644 --- a/lib/mayaUsd/render/px_vp20/utils.h +++ b/lib/mayaUsd/render/px_vp20/utils.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,14 @@ class px_vp20Utils const MHWRender::MDrawContext& context, M3dView& view); + /// Returns true if the given Maya display style indicates that a + /// bounding box should be rendered. + static bool ShouldRenderBoundingBox(unsigned int displayStyle) { + const bool boundingBoxStyle = + displayStyle & MHWRender::MFrameContext::DisplayStyle::kBoundingBox; + return boundingBoxStyle; + } + /// Renders the given bounding box in the given \p color via OpenGL. MAYAUSD_CORE_PUBLIC static bool RenderBoundingBox( @@ -74,7 +83,7 @@ class px_vp20Utils /// Helper to draw multiple wireframe boxes, where \p cubeXforms is a /// list of transforms to apply to the unit cube centered around the - /// origin. Those transforms will all be concatenated with the + /// origin. Those transforms will all be concatenated with the /// \p worldViewMat and \p projectionMat. MAYAUSD_CORE_PUBLIC static bool RenderWireCubes( diff --git a/lib/mayaUsd/render/px_vp20/utils_legacy.h b/lib/mayaUsd/render/px_vp20/utils_legacy.h index cc0148d3e9..1abeb8b392 100644 --- a/lib/mayaUsd/render/px_vp20/utils_legacy.h +++ b/lib/mayaUsd/render/px_vp20/utils_legacy.h @@ -18,9 +18,24 @@ /// \file px_vp20/utils_legacy.h +// XXX: With Maya versions up through 2019 on Linux, M3dView.h ends up +// indirectly including an X11 header that #define's "Bool" as int: +// - includes +// - includes +// - includes +// - does: "#define Bool int" +// This can cause compilation issues if is included +// afterwards, so to fix this, we ensure that it gets included first. +// +// The X11 include appears to have been removed in Maya 2020+, so this should +// no longer be an issue with later versions. +#include + #include #include +#include +#include #include #include @@ -40,6 +55,47 @@ class px_LegacyViewportUtils GfMatrix4d& viewMatrix, GfMatrix4d& projectionMatrix); + /// Helper function that converts M3dView::DisplayStyle from the legacy + /// viewport into MHWRender::MFrameContext::DisplayStyle for Viewport + /// 2.0. + /// + /// In the legacy viewport, the M3dView can be in exactly one + /// displayStyle whereas Viewport 2.0's displayStyle is a bitmask of + /// potentially multiple styles. To translate from the legacy viewport + /// to Viewport 2.0, we simply bitwise-OR the single legacy viewport + /// displayStyle into an empty mask. + static unsigned int GetMFrameContextDisplayStyle( + M3dView::DisplayStyle legacyDisplayStyle) { + unsigned int displayStyle = 0u; + + switch (legacyDisplayStyle) { + case M3dView::kBoundingBox: + displayStyle |= MHWRender::MFrameContext::DisplayStyle::kBoundingBox; + break; + case M3dView::kFlatShaded: + displayStyle |= MHWRender::MFrameContext::DisplayStyle::kFlatShaded; + break; + case M3dView::kGouraudShaded: + displayStyle |= MHWRender::MFrameContext::DisplayStyle::kGouraudShaded; + break; + case M3dView::kWireFrame: + displayStyle |= MHWRender::MFrameContext::DisplayStyle::kWireFrame; + break; + case M3dView::kPoints: + // Not supported. + break; + } + + return displayStyle; + } + + /// Returns true if the given Maya display style indicates that a + /// bounding box should be rendered. + static bool ShouldRenderBoundingBox( + M3dView::DisplayStyle legacyDisplayStyle) { + return (legacyDisplayStyle == M3dView::kBoundingBox); + } + private: px_LegacyViewportUtils() = delete; ~px_LegacyViewportUtils() = delete; diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/batchRenderer.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/batchRenderer.cpp index b9b228ce07..14775ce569 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/batchRenderer.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/batchRenderer.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -160,8 +161,7 @@ UsdMayaGLBatchRenderer::AddShapeAdapter(PxrMayaHdShapeAdapter* shapeAdapter) _shapeAdapterBuckets : _legacyShapeAdapterBuckets; - const PxrMayaHdRenderParams renderParams = - shapeAdapter->GetRenderParams(nullptr, nullptr); + const PxrMayaHdRenderParams& renderParams = shapeAdapter->GetRenderParams(); const size_t renderParamsHash = renderParams.Hash(); TF_DEBUG(PXRUSDMAYAGL_SHAPE_ADAPTER_BUCKETING).Msg( @@ -363,7 +363,8 @@ UsdMayaGLBatchRenderer::PopulateCustomPrimFilter( // Only update the collection and mark it dirty if the root paths have // actually changed. This greatly affects performance. PxrMayaHdShapeAdapter* adapter = iter->second; - const SdfPathVector& roots = adapter->GetRprimCollection().GetRootPaths(); + const HdReprSelector repr = collection.GetReprSelector(); + const SdfPathVector& roots = adapter->GetRprimCollection(repr).GetRootPaths(); if (collection.GetRootPaths() != roots) { collection.SetRootPaths(roots); changeTracker.MarkCollectionDirty(collection.GetName()); @@ -608,46 +609,26 @@ UsdMayaGLBatchRenderer::Draw(const MDrawRequest& request, M3dView& view) const PxrMayaHdUserData* hdUserData = static_cast(drawData.geometry()); - if (!hdUserData || (!hdUserData->drawShape && !hdUserData->boundingBox)) { - // Bail out as soon as possible if there's nothing to be drawn. + if (!hdUserData) { return; } + GfMatrix4d worldToViewMatrix; + _GetWorldToViewMatrix(view, &worldToViewMatrix); + MMatrix projectionMat; view.projectionMatrix(projectionMat); const GfMatrix4d projectionMatrix(projectionMat.matrix); - if (hdUserData->boundingBox) { - MMatrix modelViewMat; - view.modelViewMatrix(modelViewMat); - - // For the legacy viewport, apply a framebuffer gamma correction when - // drawing bounding boxes, just like we do when drawing geometry via - // Hydra. - glEnable(GL_FRAMEBUFFER_SRGB_EXT); - - px_vp20Utils::RenderBoundingBox(*(hdUserData->boundingBox), - *(hdUserData->wireframeColor), - modelViewMat, - projectionMat); - - glDisable(GL_FRAMEBUFFER_SRGB_EXT); - } - - if (hdUserData->drawShape) { - GfMatrix4d worldToViewMatrix; - _GetWorldToViewMatrix(view, &worldToViewMatrix); - - GfVec4d viewport; - _GetViewport(view, &viewport); + GfVec4d viewport; + _GetViewport(view, &viewport); - _RenderBatches( - /* vp2Context */ nullptr, - &view, - worldToViewMatrix, - projectionMatrix, - viewport); - } + _RenderBatches( + /* vp2Context */ nullptr, + &view, + worldToViewMatrix, + projectionMatrix, + viewport); // Clean up the user data. delete hdUserData; @@ -669,8 +650,7 @@ UsdMayaGLBatchRenderer::Draw( const PxrMayaHdUserData* hdUserData = dynamic_cast(userData); - if (!hdUserData || (!hdUserData->drawShape && !hdUserData->boundingBox)) { - // Bail out as soon as possible if there's nothing to be drawn. + if (!hdUserData) { return; } @@ -680,59 +660,134 @@ UsdMayaGLBatchRenderer::Draw( return; } + // Check whether this draw call is for a selection pass. If it is, we do + // *not* actually perform any drawing, but instead just mark a selection as + // pending so we know to re-compute selection when the next pick attempt is + // made. + // Note that Draw() calls for contexts with the "selectionPass" semantic + // are only made from draw overrides that do *not* implement user selection + // (i.e. those that do not override, or return false from, + // wantUserSelection()). The draw override for pxrHdImagingShape will + // likely be the only one of these where that is the case. + const MHWRender::MPassContext& passContext = context.getPassContext(); + const MStringArray& passSemantics = passContext.passSemantics(); + + for (unsigned int i = 0u; i < passSemantics.length(); ++i) { + if (passSemantics[i] == MHWRender::MPassContext::kSelectionPassSemantic) { + _UpdateIsSelectionPending(true); + return; + } + } + + GfMatrix4d worldToViewMatrix; + _GetWorldToViewMatrix(context, &worldToViewMatrix); + MStatus status; const MMatrix projectionMat = context.getMatrix(MHWRender::MFrameContext::kProjectionMtx, &status); const GfMatrix4d projectionMatrix(projectionMat.matrix); - if (hdUserData->boundingBox) { - const MMatrix worldViewMat = - context.getMatrix(MHWRender::MFrameContext::kWorldViewMtx, &status); + GfVec4d viewport; + _GetViewport(context, &viewport); + + M3dView view; + const bool hasView = + px_vp20Utils::GetViewFromDrawContext(context, view); + + _RenderBatches( + &context, + hasView ? &view : nullptr, + worldToViewMatrix, + projectionMatrix, + viewport); +} + +void +UsdMayaGLBatchRenderer::DrawBoundingBox( + const MDrawRequest& request, + M3dView& view) +{ + // Legacy viewport implementation. + + TRACE_FUNCTION(); + + MProfilingScope profilingScope( + ProfilerCategory, + MProfiler::kColorC_L2, + "Batch Renderer DrawBoundingBox() (Legacy Viewport)"); + + MDrawData drawData = request.drawData(); - px_vp20Utils::RenderBoundingBox(*(hdUserData->boundingBox), - *(hdUserData->wireframeColor), - worldViewMat, - projectionMat); + const PxrMayaHdUserData* hdUserData = + static_cast(drawData.geometry()); + if (!hdUserData || !hdUserData->boundingBox) { + return; } - if (hdUserData->drawShape) { - // Check whether this draw call is for a selection pass. If it is, we - // do *not* actually perform any drawing, but instead just mark a - // selection as pending so we know to re-compute selection when the - // next pick attempt is made. - // Note that Draw() calls for contexts with the "selectionPass" - // semantic are only made from draw overrides that do *not* implement - // user selection (i.e. those that do not override, or return false - // from, wantUserSelection()). The draw override for pxrHdImagingShape - // will likely be the only one of these where that is the case. - const MHWRender::MPassContext& passContext = context.getPassContext(); - const MStringArray& passSemantics = passContext.passSemantics(); - - for (unsigned int i = 0u; i < passSemantics.length(); ++i) { - if (passSemantics[i] == MHWRender::MPassContext::kSelectionPassSemantic) { - _UpdateIsSelectionPending(true); - return; - } - } + MMatrix modelViewMat; + view.modelViewMatrix(modelViewMat); + + MMatrix projectionMat; + view.projectionMatrix(projectionMat); + const GfMatrix4d projectionMatrix(projectionMat.matrix); - GfMatrix4d worldToViewMatrix; - _GetWorldToViewMatrix(context, &worldToViewMatrix); + // For the legacy viewport, apply a framebuffer gamma correction when + // drawing bounding boxes, just like we do when drawing geometry via Hydra. + glEnable(GL_FRAMEBUFFER_SRGB_EXT); - GfVec4d viewport; - _GetViewport(context, &viewport); + px_vp20Utils::RenderBoundingBox( + *(hdUserData->boundingBox), + *(hdUserData->wireframeColor), + modelViewMat, + projectionMat); - M3dView view; - const bool hasView = - px_vp20Utils::GetViewFromDrawContext(context, view); - - _RenderBatches( - &context, - hasView ? &view : nullptr, - worldToViewMatrix, - projectionMatrix, - viewport); + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + + // Clean up the user data. + delete hdUserData; +} + +void +UsdMayaGLBatchRenderer::DrawBoundingBox( + const MHWRender::MDrawContext& context, + const MUserData* userData) +{ + // Viewport 2.0 implementation. + + TRACE_FUNCTION(); + + MProfilingScope profilingScope( + ProfilerCategory, + MProfiler::kColorC_L2, + "Batch Renderer DrawBoundingBox() (Viewport 2.0)"); + + const PxrMayaHdUserData* hdUserData = + dynamic_cast(userData); + if (!hdUserData || !hdUserData->boundingBox) { + return; + } + + const MHWRender::MRenderer* theRenderer = + MHWRender::MRenderer::theRenderer(); + if (!theRenderer || !theRenderer->drawAPIIsOpenGL()) { + return; } + + MStatus status; + + const MMatrix worldViewMat = + context.getMatrix(MHWRender::MFrameContext::kWorldViewMtx, &status); + + const MMatrix projectionMat = + context.getMatrix(MHWRender::MFrameContext::kProjectionMtx, &status); + const GfMatrix4d projectionMatrix(projectionMat.matrix); + + px_vp20Utils::RenderBoundingBox( + *(hdUserData->boundingBox), + *(hdUserData->wireframeColor), + worldViewMat, + projectionMat); } GfVec2i @@ -1030,16 +1085,23 @@ UsdMayaGLBatchRenderer::_GetIntersectionPrimFilters( continue; } + // XXX: The full viewport-based collections use the "refined" repr, + // so we use the same repr here if we're doing adapter-by-adapter + // depth selection. Ideally though, this would be whatever repr was + // most recently drawn for the viewport in which the selection is + // taking place. + const HdReprSelector repr(HdReprTokens->refined); const HdRprimCollection &rprimCollection = - shapeAdapter->GetRprimCollection(); + shapeAdapter->GetRprimCollection(repr); const TfTokenVector &renderTags = shapeAdapter->GetRenderTags(); primFilters.push_back( - PxrMayaHdPrimFilter { - rprimCollection, - renderTags - }); + PxrMayaHdPrimFilter { + shapeAdapter, + rprimCollection, + renderTags + }); } } @@ -1051,6 +1113,7 @@ UsdMayaGLBatchRenderer::_GetIntersectionPrimFilters( primFilters.push_back( PxrMayaHdPrimFilter { + nullptr, collection, TfTokenVector{ #if USD_VERSION_NUM >= 1911 @@ -1241,6 +1304,7 @@ UsdMayaGLBatchRenderer::_Render( const GfMatrix4d& worldToViewMatrix, const GfMatrix4d& projectionMatrix, const GfVec4d& viewport, + unsigned int displayStyle, const std::vector<_RenderItem>& items) { TRACE_FUNCTION(); @@ -1300,7 +1364,11 @@ UsdMayaGLBatchRenderer::_Render( primFilters.size()); HdTaskSharedPtrVector renderTasks = - _taskDelegate->GetRenderTasks(paramsHash, params, primFilters); + _taskDelegate->GetRenderTasks( + paramsHash, + params, + displayStyle, + primFilters); tasks.insert(tasks.end(), renderTasks.begin(), renderTasks.end()); } @@ -1380,6 +1448,28 @@ UsdMayaGLBatchRenderer::_RenderBatches( // re-populate it. _softSelectHelper.Reset(); + // Assume shaded displayStyle, but we *should* be able to pull it from + // either the vp2Context for Viewport 2.0 or the M3dView for the legacy + // viewport. + unsigned int displayStyle = + MHWRender::MFrameContext::DisplayStyle::kGouraudShaded; + + if (vp2Context) { + displayStyle = vp2Context->getDisplayStyle(); + } else if (view3d) { + const M3dView::DisplayStyle legacyDisplayStyle = + view3d->displayStyle(); + displayStyle = + px_LegacyViewportUtils::GetMFrameContextDisplayStyle( + legacyDisplayStyle); + } + + // Since we'll be populating the prim filters with shape adapters, we don't + // need to specify collections or render tags on them, so just use empty + // ones. + static const HdRprimCollection emptyCollection; + static const TfTokenVector emptyRenderTags; + bool itemsVisible = false; std::vector<_RenderItem> items; for (const auto& iter : bucketsMap) { @@ -1392,8 +1482,9 @@ UsdMayaGLBatchRenderer::_RenderBatches( itemsVisible |= shapeAdapter->IsVisible(); primFilters.push_back(PxrMayaHdPrimFilter { - shapeAdapter->GetRprimCollection(), - shapeAdapter->GetRenderTags() + shapeAdapter, + emptyCollection, + emptyRenderTags }); } @@ -1438,6 +1529,7 @@ UsdMayaGLBatchRenderer::_RenderBatches( _Render(worldToViewMatrix, projectionMatrix, viewport, + displayStyle, items); // Viewport 2 may be rendering in multiple passes, and we want to make sure diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/batchRenderer.h b/lib/mayaUsd/render/pxrUsdMayaGL/batchRenderer.h index 126aafffbe..478d0d7bd8 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/batchRenderer.h +++ b/lib/mayaUsd/render/pxrUsdMayaGL/batchRenderer.h @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -93,8 +94,10 @@ using HgiUniquePtr = std::unique_ptr; /// A user data object should also be created/obtained for the shape by calling /// the shape adapter's GetMayaUserData() method. /// -/// In the draw stage, Draw() must be called for each draw request to complete -/// the render. +/// Typically, all Hydra-imaged shapes will be drawn automatically as a single +/// batch by the pxrHdImagingShape. However, bounding boxes are not drawn by +/// Hydra, so each draw override should invoke DrawBoundingBox() to do that if +/// necessary. /// /// Draw/selection management objects should be sure to call /// RemoveShapeAdapter() (usually in the destructor) when they no longer wish @@ -178,16 +181,26 @@ class UsdMayaGLBatchRenderer const MDagPath& dagPath, PxrMayaHdPrimFilter& primFilter); - /// Render batch or bounding box in the legacy viewport based on \p request + /// Render batch in the legacy viewport based on \p request MAYAUSD_CORE_PUBLIC void Draw(const MDrawRequest& request, M3dView& view); - /// Render batch or bounding box in Viewport 2.0 based on \p userData + /// Render batch in Viewport 2.0 based on \p userData MAYAUSD_CORE_PUBLIC void Draw( const MHWRender::MDrawContext& context, const MUserData* userData); + /// Render bounding box in the legacy viewport based on \p request + MAYAUSD_CORE_PUBLIC + void DrawBoundingBox(const MDrawRequest& request, M3dView& view); + + /// Render bounding box in Viewport 2.0 based on \p userData + MAYAUSD_CORE_PUBLIC + void DrawBoundingBox( + const MHWRender::MDrawContext& context, + const MUserData* userData); + /// Gets the resolution of the draw target used for computing selections. /// /// The resolution is specified as (width, height). @@ -306,6 +319,7 @@ class UsdMayaGLBatchRenderer const GfMatrix4d& worldToViewMatrix, const GfMatrix4d& projectionMatrix, const GfVec4d& viewport, + unsigned int displayStyle, const std::vector<_RenderItem>& items); /// Call to render all queued batches. May be called safely without diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/hdImagingShapeDrawOverride.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/hdImagingShapeDrawOverride.cpp index 657d3fe140..2885d38c45 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/hdImagingShapeDrawOverride.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/hdImagingShapeDrawOverride.cpp @@ -193,8 +193,6 @@ PxrMayaHdImagingShapeDrawOverride::prepareForDraw( newData = new PxrMayaHdUserData(); } - newData->drawShape = true; - return newData; } diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/hdImagingShapeUI.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/hdImagingShapeUI.cpp index 0dad3aa007..81b85f8b62 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/hdImagingShapeUI.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/hdImagingShapeUI.cpp @@ -116,7 +116,6 @@ PxrMayaHdImagingShapeUI::getDrawRequests( // renderer deletes the MUserData object at the end of a legacy viewport // Draw() call. PxrMayaHdUserData* userData = new PxrMayaHdUserData(); - userData->drawShape = true; MDrawData drawData; getDrawData(userData, drawData); diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/instancerImager.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/instancerImager.cpp index 7efc28d422..d7224570b6 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/instancerImager.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/instancerImager.cpp @@ -92,7 +92,7 @@ UsdMayaGL_InstancerImager::_SyncShapeAdapters( const MDagPath firstInstancePath = MDagPath::getAPathTo(handle.object()); - + // Create the adapter if it doesn't exist yet. _InstancerEntry& entry = iter->second; if (vp2) { diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/instancerShapeAdapter.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/instancerShapeAdapter.cpp index c084a2779d..6323d926cd 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/instancerShapeAdapter.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/instancerShapeAdapter.cpp @@ -17,8 +17,6 @@ #include -#include - #include #include #include @@ -28,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -43,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -75,7 +71,7 @@ bool UsdMayaGL_InstancerShapeAdapter::UpdateVisibility(const M3dView* view) { bool isVisible; - if (!_GetVisibility(_shapeDagPath, view, &isVisible)) { + if (!_GetVisibility(GetDagPath(), view, &isVisible)) { return false; } @@ -105,17 +101,6 @@ UsdMayaGL_InstancerShapeAdapter::SetRootXform(const GfMatrix4d& transform) } } -/* virtual */ -const SdfPath& -UsdMayaGL_InstancerShapeAdapter::GetDelegateID() const -{ - if (_delegate) { - return _delegate->GetDelegateID(); - } - - return SdfPath::EmptyPath(); -} - static void _ClearInstancer(const UsdGeomPointInstancer& usdInstancer) { @@ -227,7 +212,7 @@ bool UsdMayaGL_InstancerShapeAdapter::_Sync( const MDagPath& shapeDagPath, const unsigned int displayStyle, - const MHWRender::DisplayStatus displayStatus) + const MHWRender::DisplayStatus /* displayStatus */) { MStatus status; UsdPrim usdPrim = _instancerStage->GetDefaultPrim(); @@ -238,10 +223,10 @@ UsdMayaGL_InstancerShapeAdapter::_Sync( // require us to re-initialize the shape adapter. HdRenderIndex* renderIndex = UsdMayaGLBatchRenderer::GetInstance().GetRenderIndex(); - if (!(shapeDagPath == _shapeDagPath) || + if (!(shapeDagPath == GetDagPath()) || !_delegate || renderIndex != &_delegate->GetRenderIndex()) { - _shapeDagPath = shapeDagPath; + _SetDagPath(shapeDagPath); if (!_Init(renderIndex)) { return false; @@ -251,7 +236,7 @@ UsdMayaGL_InstancerShapeAdapter::_Sync( // Reset _renderParams to the defaults. _renderParams = PxrMayaHdRenderParams(); - const MMatrix transform = _shapeDagPath.inclusiveMatrix(&status); + const MMatrix transform = GetDagPath().inclusiveMatrix(&status); if (status == MS::kSuccess) { _rootXform = GfMatrix4d(transform.matrix); _delegate->SetRootTransform(_rootXform); @@ -262,43 +247,22 @@ UsdMayaGL_InstancerShapeAdapter::_Sync( // In contrast with the other shape adapters, this adapter ignores the // selection wireframe. The native instancer doesn't draw selection // wireframes, so we want to mimic that behavior for consistency. - HdReprSelector reprSelector = - GetReprSelectorForDisplayState( - displayStyle, - displayStatus); - - _drawShape = reprSelector.AnyActiveRepr(); - - // We won't ever draw the bounding box here because the native Maya - // instancer already draws a bounding box, and we don't want to draw two. - // XXX: The native Maya instancer's bounding box will only cover the native - // geometry, though; is there any way to "teach" it about our bounds? - _drawBoundingBox = false; + // XXX: This is not technically correct. Since the display style can vary + // per viewport, this decision of whether or not to enable lighting should + // be delayed until when the repr for each viewport is known during batched + // drawing. For now, the incorrectly shaded wireframe is not too offensive + // though. + // // If the repr selector specifies a wireframe-only repr, then disable // lighting. + const HdReprSelector reprSelector = + GetReprSelectorForDisplayStyle(displayStyle); if (reprSelector.Contains(HdReprTokens->wire) || reprSelector.Contains(HdReprTokens->refinedWire)) { _renderParams.enableLighting = false; } - if (_delegate->GetRootVisibility() != _drawShape) { - _delegate->SetRootVisibility(_drawShape); - } - - if (_rprimCollection.GetReprSelector() != reprSelector) { - _rprimCollection.SetReprSelector(reprSelector); - - TF_DEBUG(PXRUSDMAYAGL_SHAPE_ADAPTER_LIFECYCLE).Msg( - " Repr selector changed: %s\n" - " Marking collection dirty: %s\n", - reprSelector.GetText(), - _rprimCollection.GetName().GetText()); - - _delegate->GetRenderIndex().GetChangeTracker().MarkCollectionDirty( - _rprimCollection.GetName()); - } - HdCullStyle cullStyle = HdCullStyleNothing; if (displayStyle & MHWRender::MFrameContext::DisplayStyle::kBackfaceCulling) { cullStyle = HdCullStyleBackUnlessDoubleSided; @@ -312,79 +276,32 @@ UsdMayaGL_InstancerShapeAdapter::_Sync( bool UsdMayaGL_InstancerShapeAdapter::_Init(HdRenderIndex* renderIndex) { - if (!TF_VERIFY(renderIndex, - "Cannot initialize shape adapter with invalid HdRenderIndex")) { + if (!TF_VERIFY( + renderIndex, + "Cannot initialize shape adapter with invalid HdRenderIndex")) { return false; } - const SdfPath delegatePrefix = - UsdMayaGLBatchRenderer::GetInstance().GetDelegatePrefix(_isViewport2); - - // Create a simple "name" for this shape adapter to insert into the batch - // renderer's SdfPath hierarchy. - // - // XXX: For as long as we're using the MAYA_VP2_USE_VP1_SELECTION - // environment variable, we need to be able to pass responsibility back and - // forth between the MPxDrawOverride's shape adapter for drawing and the - // MPxSurfaceShapeUI's shape adapter for selection. This requires both - // shape adapters to have the same "name", which forces us to build it - // from data on the shape that will be common to both classes, as we do - // below. When we remove MAYA_VP2_USE_VP1_SELECTION and can trust that a - // single shape adapter handles both drawing and selection, we can do - // something even simpler instead like using the shape adapter's memory - // address as the "name". - size_t shapeHash(MObjectHandle(_shapeDagPath.transform()).hashCode()); - boost::hash_combine(shapeHash, _instancerStage->GetDefaultPrim()); - - // We prepend the Maya type name to the beginning of the delegate name to - // ensure that there are no name collisions between shape adapters of - // shapes with different Maya types. - const TfToken delegateName( - TfStringPrintf("%s_%zx", - _tokens->NativeInstancerType.GetText(), - shapeHash)); - - const SdfPath delegateId = delegatePrefix.AppendChild(delegateName); - - if (_delegate && - delegateId == GetDelegateID() && - renderIndex == &_delegate->GetRenderIndex()) { - // The delegate's current ID matches the delegate ID we computed and - // the render index matches, so it must be up to date already. - return true; - } - - const TfToken collectionName = _GetRprimCollectionName(); - TF_DEBUG(PXRUSDMAYAGL_SHAPE_ADAPTER_LIFECYCLE).Msg( "Initializing UsdMayaGL_InstancerShapeAdapter: %p\n" - " shape DAG path : %s\n" - " collection name: %s\n" - " delegateId : %s\n", + " shape DAG path : %s\n" + " shape identifier: %s\n" + " delegateId : %s\n", this, - _shapeDagPath.fullPathName().asChar(), - collectionName.GetText(), - delegateId.GetText()); + GetDagPath().fullPathName().asChar(), + _shapeIdentifier.GetText(), + _delegateId.GetText()); - _delegate.reset(new UsdImagingDelegate(renderIndex, delegateId)); + _delegate.reset(new UsdImagingDelegate(renderIndex, _delegateId)); if (!TF_VERIFY(_delegate, "Failed to create shape adapter delegate for shape %s", - _shapeDagPath.fullPathName().asChar())) { + GetDagPath().fullPathName().asChar())) { return false; } UsdPrim usdPrim = _instancerStage->GetDefaultPrim(); _delegate->Populate(usdPrim, SdfPathVector()); - if (collectionName != _rprimCollection.GetName()) { - _rprimCollection.SetName(collectionName); - renderIndex->GetChangeTracker().AddCollection( - _rprimCollection.GetName()); - } - - _rprimCollection.SetReprSelector(HdReprSelector(HdReprTokens->refined)); - _rprimCollection.SetRootPath(delegateId); - return true; } @@ -392,7 +309,7 @@ UsdMayaGL_InstancerShapeAdapter::_Init(HdRenderIndex* renderIndex) void UsdMayaGL_InstancerShapeAdapter::SyncInstancerPerPrototypePostHook( const MPlug& , UsdPrim& prototypePrim, - std::vector& + std::vector& ) { UsdReferences prototypeRefs = prototypePrim.GetReferences(); diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/instancerShapeAdapter.h b/lib/mayaUsd/render/pxrUsdMayaGL/instancerShapeAdapter.h index 84669075d0..fceba3dd33 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/instancerShapeAdapter.h +++ b/lib/mayaUsd/render/pxrUsdMayaGL/instancerShapeAdapter.h @@ -75,7 +75,7 @@ class UsdMayaGL_InstancerShapeAdapter : public PxrMayaHdShapeAdapter /// Gets whether the shape adapter's shape is visible. /// /// This should be called after a call to UpdateVisibility() to ensure - /// that the returned value is correct. + /// that the returned value is correct. MAYAUSD_CORE_PUBLIC bool IsVisible() const override; @@ -83,10 +83,7 @@ class UsdMayaGL_InstancerShapeAdapter : public PxrMayaHdShapeAdapter void SetRootXform(const GfMatrix4d& transform) override; MAYAUSD_CORE_PUBLIC - const SdfPath& GetDelegateID() const override; - - MAYAUSD_CORE_PUBLIC - ~UsdMayaGL_InstancerShapeAdapter() override; + ~UsdMayaGL_InstancerShapeAdapter() override; protected: @@ -126,9 +123,10 @@ class UsdMayaGL_InstancerShapeAdapter : public PxrMayaHdShapeAdapter /// Initialize the shape adapter using the given \p renderIndex. /// /// This method is called automatically during Sync() when the shape - /// adapter's "identity" changes. This happens when the delegateId or - /// the rprim collection name computed from the shape adapter's shape - /// is different than what is currently stored in the shape adapter. + /// adapter's "identity" changes. This can happen when the shape + /// managed by this adapter is changed by setting a new DAG path, or + /// otherwise when there is some other fundamental change to the shape + /// or to the delegate or render index. /// The shape adapter will then query the batch renderer for its render /// index and use that to re-create its delegate and re-add its rprim /// collection, if necessary. diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/proxyDrawOverride.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/proxyDrawOverride.cpp index 5429ae97d6..a52dc0f248 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/proxyDrawOverride.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/proxyDrawOverride.cpp @@ -208,24 +208,9 @@ UsdMayaProxyDrawOverride::prepareForDraw( UsdMayaGLBatchRenderer::GetInstance().AddShapeAdapter(&_shapeAdapter); - bool drawShape; - bool drawBoundingBox; - _shapeAdapter.GetRenderParams(&drawShape, &drawBoundingBox); + const MBoundingBox boundingBox = shape->boundingBox(); - if (!drawBoundingBox && !drawShape) { - // We weren't asked to do anything. - return nullptr; - } - - MBoundingBox boundingBox; - MBoundingBox* boundingBoxPtr = nullptr; - if (drawBoundingBox) { - // Only query for the bounding box if we're drawing it. - boundingBox = shape->boundingBox(); - boundingBoxPtr = &boundingBox; - } - - return _shapeAdapter.GetMayaUserData(oldData, boundingBoxPtr); + return _shapeAdapter.GetMayaUserData(oldData, &boundingBox); } /* virtual */ @@ -269,7 +254,7 @@ UsdMayaProxyDrawOverride::userSelect( const unsigned int displayStyle = context.getDisplayStyle(); const MHWRender::DisplayStatus displayStatus = - MHWRender::MGeometryUtilities::displayStatus(_shapeAdapter._shapeDagPath); + MHWRender::MGeometryUtilities::displayStatus(_shapeAdapter.GetDagPath()); // At this point, we expect the shape to have already been drawn and our // shape adapter to have been added to the batch renderer, but just in @@ -278,7 +263,7 @@ UsdMayaProxyDrawOverride::userSelect( // must have already been done to have caused the shape to be drawn and // become eligible for selection. if (!_shapeAdapter.Sync( - _shapeAdapter._shapeDagPath, displayStyle, displayStatus)) { + _shapeAdapter.GetDagPath(), displayStyle, displayStatus)) { return false; } @@ -314,11 +299,12 @@ UsdMayaProxyDrawOverride::draw( MProfiler::kColorC_L1, "USD Proxy Shape draw() (Viewport 2.0)"); - // Note that this Draw() call is only necessary when we're drawing the - // bounding box, since that is not yet handled by Hydra and is instead done - // internally by the batch renderer on a per-shape basis. Otherwise, the - // pxrHdImagingShape is what will invoke Hydra to draw the shape. - UsdMayaGLBatchRenderer::GetInstance().Draw(context, data); + const unsigned int displayStyle = context.getDisplayStyle(); + if (!px_vp20Utils::ShouldRenderBoundingBox(displayStyle)) { + return; + } + + UsdMayaGLBatchRenderer::GetInstance().DrawBoundingBox(context, data); } diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/proxyShapeDelegate.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/proxyShapeDelegate.cpp index c44d275515..d11f4406e3 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/proxyShapeDelegate.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/proxyShapeDelegate.cpp @@ -33,9 +33,10 @@ PXR_NAMESPACE_OPEN_SCOPE static PxrMayaHdPrimFilter _sharedPrimFilter = { + nullptr, HdRprimCollection( - TfToken("UsdMayaGL_ClosestPointOnProxyShape"), - HdReprSelector(HdReprTokens->refined) + TfToken("UsdMayaGL_ClosestPointOnProxyShape"), + HdReprSelector(HdReprTokens->refined) ), TfTokenVector() // Render Tags }; diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/proxyShapeUI.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/proxyShapeUI.cpp index 127668d8f9..9aa901b15e 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/proxyShapeUI.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/proxyShapeUI.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -85,26 +86,11 @@ UsdMayaProxyShapeUI::getDrawRequests( UsdMayaGLBatchRenderer::GetInstance().AddShapeAdapter(&_shapeAdapter); - bool drawShape; - bool drawBoundingBox; - _shapeAdapter.GetRenderParams(&drawShape, &drawBoundingBox); - - if (!drawBoundingBox && !drawShape) { - // We weren't asked to do anything. - return; - } - - MBoundingBox boundingBox; - MBoundingBox* boundingBoxPtr = nullptr; - if (drawBoundingBox) { - // Only query for the bounding box if we're drawing it. - boundingBox = shape->boundingBox(); - boundingBoxPtr = &boundingBox; - } + const MBoundingBox boundingBox = shape->boundingBox(); MDrawRequest request = drawInfo.getPrototype(*this); - _shapeAdapter.GetMayaUserData(this, request, boundingBoxPtr); + _shapeAdapter.GetMayaUserData(this, request, &boundingBox); // Add the request to the queue. requests.add(request); @@ -121,17 +107,18 @@ UsdMayaProxyShapeUI::draw(const MDrawRequest& request, M3dView& view) const MProfiler::kColorC_L1, "USD Proxy Shape draw() (Legacy Viewport)"); + const M3dView::DisplayStyle legacyDisplayStyle = view.displayStyle(); + if (!px_LegacyViewportUtils::ShouldRenderBoundingBox(legacyDisplayStyle)) { + return; + } + if (!view.pluginObjectDisplay(MayaUsdProxyShapeBase::displayFilterName)) { return; } - // Note that this Draw() call is only necessary when we're drawing the - // bounding box, since that is not yet handled by Hydra and is instead done - // internally by the batch renderer on a per-shape basis. Otherwise, the - // pxrHdImagingShape is what will invoke Hydra to draw the shape. view.beginGL(); - UsdMayaGLBatchRenderer::GetInstance().Draw(request, view); + UsdMayaGLBatchRenderer::GetInstance().DrawBoundingBox(request, view); view.endGL(); } diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/renderParams.h b/lib/mayaUsd/render/pxrUsdMayaGL/renderParams.h index c0093d01ac..a8e9a94632 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/renderParams.h +++ b/lib/mayaUsd/render/pxrUsdMayaGL/renderParams.h @@ -35,12 +35,14 @@ struct PxrMayaHdRenderParams // Color Params // + bool useWireframe = false; GfVec4f wireframeColor = GfVec4f(0.0f); /// Helper function to find a batch key for the render params size_t Hash() const { size_t hash = size_t(enableLighting); + boost::hash_combine(hash, useWireframe); boost::hash_combine(hash, wireframeColor); return hash; diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/sceneDelegate.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/sceneDelegate.cpp index 6d4c9baecf..8f1101219d 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/sceneDelegate.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/sceneDelegate.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #include #include #include +#include PXR_NAMESPACE_OPEN_SCOPE @@ -62,7 +64,6 @@ TF_DEFINE_PRIVATE_TOKENS( _tokens, (selectionTask) - (renderTags) ); @@ -120,6 +121,7 @@ PxrMayaHdSceneDelegate::PxrMayaHdSceneDelegate( _simpleLightTaskId = _rootId.AppendChild(HdxPrimitiveTokens->simpleLightTask); _shadowTaskId = _rootId.AppendChild(HdxPrimitiveTokens->shadowTask); _pickingTaskId = _rootId.AppendChild(HdxPrimitiveTokens->pickTask); + _selectionTaskId = _rootId.AppendChild(_tokens->selectionTask); _cameraId = _rootId.AppendChild(HdPrimTypeTokens->camera); // camera @@ -163,7 +165,7 @@ PxrMayaHdSceneDelegate::PxrMayaHdSceneDelegate( taskParams.camera = _cameraId; taskParams.viewport = _viewport; cache[HdTokens->params] = VtValue(taskParams); - cache[_tokens->renderTags] = VtValue(defaultShadowRenderTags); + cache[HdTokens->renderTags] = VtValue(defaultShadowRenderTags); } // Picking task. @@ -178,7 +180,23 @@ PxrMayaHdSceneDelegate::PxrMayaHdSceneDelegate( // on first use, but this ensures we don't need // to special case first time vs others for comparing // to current render tags - cache[_tokens->renderTags] = VtValue(TfTokenVector()); + cache[HdTokens->renderTags] = VtValue(TfTokenVector()); + } + + // Selection task. + { + renderIndex->InsertTask(this, _selectionTaskId); + _ValueCache& cache = _valueCacheMap[_selectionTaskId]; + HdxSelectionTaskParams taskParams; + taskParams.enableSelection = true; + + // Note that the selection color is a constant zero value. This is to + // mimic selection behavior in Maya where the wireframe color is what + // changes to indicate selection and not the object color. + taskParams.selectionColor = GfVec4f(0.0f); + + cache[HdTokens->params] = VtValue(taskParams); + cache[HdTokens->collection] = VtValue(); } } @@ -211,7 +229,7 @@ PxrMayaHdSceneDelegate::GetCameraParamValue( TfTokenVector PxrMayaHdSceneDelegate::GetTaskRenderTags(SdfPath const& taskId) { - VtValue value = Get(taskId, _tokens->renderTags); + VtValue value = Get(taskId, HdTokens->renderTags); return value.Get(); } @@ -435,40 +453,19 @@ PxrMayaHdSceneDelegate::GetSetupTasks() return tasks; } -HdTaskSharedPtrVector -PxrMayaHdSceneDelegate::GetPickingTasks( - const TfTokenVector& renderTags) -{ - // Update tasks render tags to match those specified in the parameter. - const TfTokenVector ¤tRenderTags = - _GetValue(_pickingTaskId, _tokens->renderTags); - - if (currentRenderTags != renderTags) { - _SetValue(_pickingTaskId, _tokens->renderTags, renderTags); - GetRenderIndex().GetChangeTracker().MarkTaskDirty( - _pickingTaskId, - HdChangeTracker::DirtyRenderTags); - } - - HdTaskSharedPtrVector tasks; - - tasks.push_back(GetRenderIndex().GetTask(_pickingTaskId)); - - return tasks; -} - HdTaskSharedPtrVector PxrMayaHdSceneDelegate::GetRenderTasks( const size_t hash, const PxrMayaHdRenderParams& renderParams, + unsigned int displayStyle, const PxrMayaHdPrimFilterVector& primFilters) { HdTaskSharedPtrVector taskList; - HdRenderIndex &renderIndex = GetRenderIndex(); + HdRenderIndex& renderIndex = GetRenderIndex(); // Task List Consist of: // Render Setup Task - // Render Task Per Collection + // Render Task Per Shape Adapter/Collection // Selection Task taskList.reserve(2 + primFilters.size()); @@ -503,86 +500,83 @@ PxrMayaHdSceneDelegate::GetRenderTasks( } taskList.emplace_back(renderIndex.GetTask(renderSetupTaskId)); - size_t numPrimFilters = primFilters.size(); - for (size_t primFilterNum = 0; - primFilterNum < numPrimFilters; - ++primFilterNum) + for (const PxrMayaHdPrimFilter& primFilter : primFilters) { + SdfPath renderTaskId; + HdRprimCollection rprimCollection; + TfTokenVector renderTags; - { - const PxrMayaHdPrimFilter &primFilter = primFilters[primFilterNum]; + if (primFilter.shapeAdapter != nullptr) { + const HdReprSelector repr = + primFilter.shapeAdapter->GetReprSelectorForDisplayStyle( + displayStyle); - _RenderTaskIdMapKey key = {hash, primFilter.collection.GetName()}; + if (!repr.AnyActiveRepr()) { + continue; + } - SdfPath renderTaskId; - if (!TfMapLookup(_renderTaskIdMap, key, &renderTaskId)) { - // Create a new render task if one does not exist for this key. - // Note that we expect the collection name to have already been - // sanitized for use in SdfPaths. - TF_VERIFY(TfIsValidIdentifier(key.collectionName.GetString())); - renderTaskId = _rootId.AppendChild( - TfToken(TfStringPrintf("%s_%zx_%s", - HdxPrimitiveTokens->renderTask.GetText(), - key.hash, - key.collectionName.GetText()))); - - GetRenderIndex().InsertTask(this, renderTaskId); + renderTaskId = primFilter.shapeAdapter->GetRenderTaskId(repr); + rprimCollection = primFilter.shapeAdapter->GetRprimCollection(repr); + renderTags = primFilter.shapeAdapter->GetRenderTags(); + } else { + rprimCollection = primFilter.collection; + renderTags = primFilter.renderTags; + + // The batch renderer manages the render task ID for this + // collection, so look up its ID by name. + const TfToken& collectionName = rprimCollection.GetName(); + + if (!TfMapLookup(_renderTaskIdMap, collectionName, &renderTaskId)) { + // Create a new render task ID if one does not exist for this + // collection. + // Note that we expect the collection name to have already been + // sanitized for use in SdfPaths. + TF_VERIFY(TfIsValidIdentifier(collectionName.GetString())); + renderTaskId = _rootId.AppendChild( + TfToken( + TfStringPrintf( + "%s_%s", + HdxPrimitiveTokens->renderTask.GetText(), + collectionName.GetText()))); + + _renderTaskIdMap[collectionName] = renderTaskId; + } + } + + HdTaskSharedPtr renderTask = renderIndex.GetTask(renderTaskId); + if (!renderTask) { + renderIndex.InsertTask(this, renderTaskId); + renderTask = renderIndex.GetTask(renderTaskId); + + _ValueCache& cache = _valueCacheMap[renderTaskId]; // Note that the render task has no params of its own. All of the // render params are on the render setup task instead. - _ValueCache& cache = _valueCacheMap[renderTaskId]; cache[HdTokens->params] = VtValue(); - cache[HdTokens->collection] = VtValue(primFilter.collection); - cache[_tokens->renderTags] = VtValue(primFilter.renderTags); - _renderTaskIdMap[key] = renderTaskId; + // Once the task is created, the batch renderer itself will not + // change the task's collection. + cache[HdTokens->collection] = VtValue(rprimCollection); + + cache[HdTokens->renderTags] = VtValue(renderTags); } else { // Update task's render tags - const TfTokenVector ¤tRenderTags = - _GetValue(renderTaskId, _tokens->renderTags); + const TfTokenVector& currentRenderTags = + _GetValue(renderTaskId, HdTokens->renderTags); - if (currentRenderTags != primFilter.renderTags) { - _SetValue(renderTaskId, _tokens->renderTags, primFilter.renderTags); - GetRenderIndex().GetChangeTracker().MarkTaskDirty( - renderTaskId, + if (currentRenderTags != renderTags) { + _SetValue(renderTaskId, HdTokens->renderTags, renderTags); + renderIndex.GetChangeTracker().MarkTaskDirty( + renderTaskId, HdChangeTracker::DirtyRenderTags); } } - taskList.emplace_back(renderIndex.GetTask(renderTaskId)); - - // Update the collections on the render task and mark them dirty. - // XXX: Should only mark collection dirty if collection has changed - _SetValue(renderTaskId, HdTokens->collection, primFilter.collection); - GetRenderIndex().GetChangeTracker().MarkTaskDirty( - renderTaskId, - HdChangeTracker::DirtyCollection); + if (renderTask) { + taskList.emplace_back(renderTask); + } } - SdfPath selectionTaskId; - if (!TfMapLookup(_selectionTaskIdMap, hash, &selectionTaskId)) { - // Create a new selection task if one does not exist for this hash. - selectionTaskId = _rootId.AppendChild( - TfToken(TfStringPrintf("%s_%zx", - _tokens->selectionTask.GetText(), - hash))); - - GetRenderIndex().InsertTask(this, selectionTaskId); - HdxSelectionTaskParams selectionTaskParams; - selectionTaskParams.enableSelection = true; - - // Note that the selection color is a constant zero value. This is to - // mimic selection behavior in Maya where the wireframe color is what - // changes to indicate selection and not the object color. As a result, - // we don't need to dirty the selectionTaskParams below. - selectionTaskParams.selectionColor = GfVec4f(0.0f); - - _ValueCache& cache = _valueCacheMap[selectionTaskId]; - cache[HdTokens->params] = VtValue(selectionTaskParams); - cache[HdTokens->collection] = VtValue(); - - _selectionTaskIdMap[hash] = selectionTaskId; - } - taskList.emplace_back(renderIndex.GetTask(selectionTaskId)); + taskList.emplace_back(renderIndex.GetTask(_selectionTaskId)); // // (meta-XXX): The notes below are actively being addressed with an @@ -601,20 +595,10 @@ PxrMayaHdSceneDelegate::GetRenderTasks( // With Hydra, changing the contents of a collection can be // an expensive operation as it causes draw batches to be rebuilt. // - // The Maya-Hydra Plugin is currently reusing the same collection - // name for all collections within a frame. - // (This stems from a time when collection name had a significant meaning - // rather than id'ing a collection). - // // The plugin should also track deltas to the contents of a collection // and set Hydra's dirty state when prims get added and removed from // the collection. // - // Another possible change that can be made to this code is HdxRenderTask - // now takes an array of collections, so it is possible to support different - // reprs using the same task. Therefore, this code should be modified to - // only add one task that is provided with the active set of collections. - // // However, a further improvement to the code could be made using // UsdDelegate's fallback repr feature instead of using multiple // collections as it would avoid modifying the collection as a Maya shape @@ -635,7 +619,7 @@ PxrMayaHdSceneDelegate::GetRenderTasks( // Store the updated render setup task params back in the cache and // mark them dirty. _SetValue(renderSetupTaskId, HdTokens->params, renderSetupTaskParams); - GetRenderIndex().GetChangeTracker().MarkTaskDirty( + renderIndex.GetChangeTracker().MarkTaskDirty( renderSetupTaskId, HdChangeTracker::DirtyParams); } @@ -643,23 +627,27 @@ PxrMayaHdSceneDelegate::GetRenderTasks( return taskList; } - -size_t -PxrMayaHdSceneDelegate::_RenderTaskIdMapKey::HashFunctor::operator ()( - const _RenderTaskIdMapKey& value) const +HdTaskSharedPtrVector +PxrMayaHdSceneDelegate::GetPickingTasks( + const TfTokenVector& renderTags) { - size_t hash = value.hash; - boost::hash_combine(hash, value.collectionName); + // Update tasks render tags to match those specified in the parameter. + const TfTokenVector ¤tRenderTags = + _GetValue(_pickingTaskId, HdTokens->renderTags); - return hash; -} + if (currentRenderTags != renderTags) { + _SetValue(_pickingTaskId, HdTokens->renderTags, renderTags); + GetRenderIndex().GetChangeTracker().MarkTaskDirty( + _pickingTaskId, + HdChangeTracker::DirtyRenderTags); + } -bool -PxrMayaHdSceneDelegate::_RenderTaskIdMapKey::operator ==( - const _RenderTaskIdMapKey& other) const -{ - return ((this->hash == other.hash) && - (this->collectionName == other.collectionName)); + HdTaskSharedPtrVector tasks; + + tasks.push_back(GetRenderIndex().GetTask(_pickingTaskId)); + + return tasks; } + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/sceneDelegate.h b/lib/mayaUsd/render/pxrUsdMayaGL/sceneDelegate.h index c71a5358a7..08b950104a 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/sceneDelegate.h +++ b/lib/mayaUsd/render/pxrUsdMayaGL/sceneDelegate.h @@ -40,10 +40,21 @@ #include #include +#include PXR_NAMESPACE_OPEN_SCOPE +/// Prim filters can be specified in one of two ways: +/// +/// 1. If a shape adapter is being used, it can be specified in the +/// shapeAdapter field and all necessary data will be obtained by +/// querying the shape adapter for it. +/// 2. If no shape adapter is being used, the shapeAdapter field should be +/// set to nullptr, and a collection and set of render tags *must* be +/// provided. +/// struct PxrMayaHdPrimFilter { + PxrMayaHdShapeAdapter* shapeAdapter; HdRprimCollection collection; TfTokenVector renderTags; }; @@ -94,6 +105,7 @@ class PxrMayaHdSceneDelegate : public HdSceneDelegate HdTaskSharedPtrVector GetRenderTasks( const size_t hash, const PxrMayaHdRenderParams& renderParams, + unsigned int displayStyle, const PxrMayaHdPrimFilterVector& primFilters); MAYAUSD_CORE_PUBLIC @@ -133,37 +145,26 @@ class PxrMayaHdSceneDelegate : public HdSceneDelegate SdfPath _shadowTaskId; - // XXX: While this is correct, that we are using - // hash in forming the task id, so the map is valid. - // It is possible for the hash to collide, so the id - // formed from the combination of hash and collection name is not - // necessarily unique. - struct _RenderTaskIdMapKey - { - size_t hash; - TfToken collectionName; + // When prim filters are populated including a shape adapter, the + // adapter is responsible for providing the appropriate render task ID + // for a given repr. When no shape adapter is given, the batch renderer + // manages the render task ID and constructs it using the rprim + // collection name. The batch renderer will ultimately instantiate the + // render task itself for both cases. + // This type maps collection names to render task IDs for tasks in the + // latter case where the task ID is managed by the batch renderer. + using _RenderTaskIdMap = + std::unordered_map; + + // For render setup tasks, there is one task per unique set of render + // params, which are hashed to generate a key. + using _RenderParamTaskIdMap = std::unordered_map; - struct HashFunctor { - size_t operator()(const _RenderTaskIdMapKey& value) const; - }; - - bool operator==(const _RenderTaskIdMapKey& other) const; - }; - - typedef std::unordered_map< - _RenderTaskIdMapKey, - SdfPath, - _RenderTaskIdMapKey::HashFunctor> _RenderTaskIdMap; - - typedef std::unordered_map _RenderParamTaskIdMap; - - - _RenderParamTaskIdMap _renderSetupTaskIdMap; _RenderTaskIdMap _renderTaskIdMap; - _RenderParamTaskIdMap _selectionTaskIdMap; - SdfPath _pickingTaskId; + SdfPath _pickingTaskId; + SdfPath _selectionTaskId; typedef TfHashMap _ValueCache; typedef TfHashMap _ValueCacheMap; diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/shapeAdapter.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/shapeAdapter.cpp index b9345f26b7..f9599f9396 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/shapeAdapter.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/shapeAdapter.cpp @@ -15,13 +15,16 @@ // #include "shapeAdapter.h" +#include +#include + #include #include #include #include #include #include -#include +#include #include #include #include @@ -40,9 +43,13 @@ #include #include #include +#include +#include +#include #include #include +#include #include #include #include @@ -51,40 +58,6 @@ PXR_NAMESPACE_OPEN_SCOPE -// Helper function that converts M3dView::DisplayStyle (legacy viewport) into -// MHWRender::MFrameContext::DisplayStyle (Viewport 2.0). -// -// In the legacy viewport, the M3dView can be in exactly one displayStyle -// whereas Viewport 2.0's displayStyle is a bitmask of potentially multiple -// styles. To translate from the legacy viewport to Viewport 2.0, we simply -// bitwise-OR the single legacy viewport displayStyle into an empty mask. -static inline -unsigned int -_ToMFrameContextDisplayStyle(const M3dView::DisplayStyle legacyDisplayStyle) -{ - unsigned int displayStyle = 0u; - - switch (legacyDisplayStyle) { - case M3dView::kBoundingBox: - displayStyle |= MHWRender::MFrameContext::DisplayStyle::kBoundingBox; - break; - case M3dView::kFlatShaded: - displayStyle |= MHWRender::MFrameContext::DisplayStyle::kFlatShaded; - break; - case M3dView::kGouraudShaded: - displayStyle |= MHWRender::MFrameContext::DisplayStyle::kGouraudShaded; - break; - case M3dView::kWireFrame: - displayStyle |= MHWRender::MFrameContext::DisplayStyle::kWireFrame; - break; - case M3dView::kPoints: - // Not supported. - break; - } - - return displayStyle; -} - // Helper function that converts M3dView::DisplayStatus (legacy viewport) into // MHWRender::DisplayStatus (Viewport 2.0). static inline @@ -110,7 +83,18 @@ _ToMHWRenderDisplayStatus(const M3dView::DisplayStatus legacyDisplayStatus) return MHWRender::DisplayStatus((int)legacyDisplayStatus); } -/* virtual */ +static inline +bool +_IsActiveDisplayStatus(MHWRender::DisplayStatus displayStatus) +{ + return + (displayStatus == MHWRender::DisplayStatus::kActive) || + (displayStatus == MHWRender::DisplayStatus::kHilite) || + (displayStatus == MHWRender::DisplayStatus::kActiveTemplate) || + (displayStatus == MHWRender::DisplayStatus::kActiveComponent) || + (displayStatus == MHWRender::DisplayStatus::kLead); +} + bool PxrMayaHdShapeAdapter::Sync( const MDagPath& shapeDagPath, @@ -122,7 +106,8 @@ PxrMayaHdShapeAdapter::Sync( UsdMayaGLBatchRenderer::GetInstance().StartBatchingFrameDiagnostics(); const unsigned int displayStyle = - _ToMFrameContextDisplayStyle(legacyDisplayStyle); + px_LegacyViewportUtils::GetMFrameContextDisplayStyle( + legacyDisplayStyle); const MHWRender::DisplayStatus displayStatus = _ToMHWRenderDisplayStatus(legacyDisplayStatus); @@ -152,7 +137,6 @@ PxrMayaHdShapeAdapter::Sync( return success; } -/* virtual */ bool PxrMayaHdShapeAdapter::Sync( const MDagPath& shapeDagPath, @@ -214,31 +198,15 @@ PxrMayaHdShapeAdapter::GetMayaUserData( // Viewport 2.0 implementation (also called by legacy viewport // implementation). // - // Our PxrMayaHdUserData can be used to signify whether we are requesting a - // shape to be rendered, a bounding box, both, or neither. - // // In the Viewport 2.0 prepareForDraw() usage, any MUserData object passed // into the function will be deleted by Maya. In the legacy viewport usage, // the object gets deleted at the end of a legacy viewport Draw() call. - if (!_drawShape && !boundingBox) { - return nullptr; - } - PxrMayaHdUserData* newData = dynamic_cast(oldData); if (!newData) { newData = new PxrMayaHdUserData(); } - // Internally, the shape adapter keeps track of whether its shape is being - // drawn for managing visibility, but otherwise most Hydra-imaged shapes - // should not be drawing themselves. The pxrHdImagingShape will take care - // of batching up the drawing of all of the shapes, so we specify in the - // Maya user data that the shape should *not* draw by default. The - // pxrHdImagingShape bypasses this and sets drawShape to true. - // We handle this similarly in GetRenderParams() below. - newData->drawShape = false; - if (boundingBox) { newData->boundingBox.reset(new MBoundingBox(*boundingBox)); newData->wireframeColor.reset(new GfVec4f(_renderParams.wireframeColor)); @@ -250,11 +218,9 @@ PxrMayaHdShapeAdapter::GetMayaUserData( return newData; } -/* virtual */ HdReprSelector -PxrMayaHdShapeAdapter::GetReprSelectorForDisplayState( - const unsigned int displayStyle, - const MHWRender::DisplayStatus displayStatus) const +PxrMayaHdShapeAdapter::GetReprSelectorForDisplayStyle( + unsigned int displayStyle) const { HdReprSelector reprSelector; @@ -272,18 +238,17 @@ PxrMayaHdShapeAdapter::GetReprSelectorForDisplayState( return reprSelector; } + const MHWRender::DisplayStatus displayStatus = + MHWRender::MGeometryUtilities::displayStatus(_shapeDagPath); + + const bool isActive = _IsActiveDisplayStatus(displayStatus); + const bool shadeActiveOnlyStyle = displayStyle & MHWRender::MFrameContext::DisplayStyle::kShadeActiveOnly; - const bool isActive = - (displayStatus == MHWRender::DisplayStatus::kActive) || - (displayStatus == MHWRender::DisplayStatus::kHilite) || - (displayStatus == MHWRender::DisplayStatus::kActiveTemplate) || - (displayStatus == MHWRender::DisplayStatus::kActiveComponent) || - (displayStatus == MHWRender::DisplayStatus::kLead); - const bool wireframeStyle = - displayStyle & MHWRender::MFrameContext::DisplayStyle::kWireFrame; + (displayStyle & MHWRender::MFrameContext::DisplayStyle::kWireFrame) || + _renderParams.useWireframe; const bool flatShadedStyle = displayStyle & MHWRender::MFrameContext::DisplayStyle::kFlatShaded; @@ -326,130 +291,152 @@ PxrMayaHdShapeAdapter::GetReprSelectorForDisplayState( return reprSelector; } -/* virtual */ -PxrMayaHdRenderParams -PxrMayaHdShapeAdapter::GetRenderParams( - bool* drawShape, - bool* drawBoundingBox) const +MStatus +PxrMayaHdShapeAdapter::_SetDagPath(const MDagPath& dagPath) { - if (drawShape) { - // Internally, the shape adapter keeps track of whether its shape is - // being drawn for managing visibility, but otherwise most Hydra-imaged - // shapes should not be drawing themselves. The pxrHdImagingShape will - // take care of batching up the drawing of all of the shapes, so for - // the purposes of render params, we set drawShape to false by default. - // The pxrHdImagingShape bypasses this and sets drawShape to true. - // We handle this similarly in GetMayaUserData() above. - *drawShape = false; + if (_shapeDagPath == dagPath) { + return MS::kSuccess; } - if (drawBoundingBox) { - *drawBoundingBox = _drawBoundingBox; - } + _shapeDagPath = dagPath; - return _renderParams; -} + _shapeIdentifier = TfToken(); + _delegateId = SdfPath(); -/* virtual */ -const HdRprimCollection& -PxrMayaHdShapeAdapter::GetRprimCollection() const -{ - return _rprimCollection; -} + _rprimCollectionMap.clear(); + _renderTaskIdMap.clear(); -/* virtual */ -const TfTokenVector& -PxrMayaHdShapeAdapter::GetRenderTags() const -{ - return _renderTags; -} + MStatus status; + const bool dagPathIsValid = _shapeDagPath.isValid(&status); + if (status != MS::kSuccess || !dagPathIsValid) { + return status; + } + const MFnDagNode dagNodeFn(_shapeDagPath, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + // We use the shape's UUID as a unique identifier for this shape adapter in + // the batch renderer's SdfPath hierarchy. Since MPxDrawOverrides may be + // constructed and destructed over the course of a node's lifetime (e.g. by + // doing 'ogs -reset'), we need an identifier tied to the node's lifetime + // rather than to the shape adapter's lifetime. + const MUuid shapeUuid = dagNodeFn.uuid(&status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const std::string uuidString( + shapeUuid.asString().asChar(), + shapeUuid.asString().length()); + + _shapeIdentifier = TfToken(TfMakeValidIdentifier(uuidString)); + + const UsdMayaGLBatchRenderer& batchRenderer = + UsdMayaGLBatchRenderer::GetInstance(); + HdRenderIndex* renderIndex = batchRenderer.GetRenderIndex(); + + const SdfPath delegatePrefix = + batchRenderer.GetDelegatePrefix(_isViewport2); + + _delegateId = delegatePrefix.AppendChild(_shapeIdentifier); + + // Create entries in the collection and render task ID maps for each repr + // we might use. + static const std::vector allMayaReprs({ + HdReprSelector(HdReprTokens->hull), + HdReprSelector(HdReprTokens->refined), + HdReprSelector(HdReprTokens->refinedWire), + HdReprSelector(HdReprTokens->refinedWireOnSurf), + HdReprSelector(HdReprTokens->wire), + HdReprSelector(HdReprTokens->wireOnSurf) + }); + + for (const HdReprSelector& reprSelector : allMayaReprs) { + const TfToken rprimCollectionName = TfToken( + TfStringPrintf("%s_%s", + _shapeIdentifier.GetText(), + reprSelector.GetText())); + _rprimCollectionMap.emplace( + std::make_pair( + reprSelector, + HdRprimCollection( + rprimCollectionName, + reprSelector, + _delegateId))); + + renderIndex->GetChangeTracker().AddCollection(rprimCollectionName); + + // We only generate the render task ID here. We'll leave it to the + // batch renderer to lazily create the task in the render index when + // it is first needed. + const TfToken renderTaskName = TfToken( + TfStringPrintf("%s_%s", + HdxPrimitiveTokens->renderTask.GetText(), + rprimCollectionName.GetText())); + _renderTaskIdMap.emplace( + std::make_pair( + reprSelector, + _delegateId.AppendChild(renderTaskName))); + } -/* virtual */ -const GfMatrix4d& -PxrMayaHdShapeAdapter::GetRootXform() const -{ - return _rootXform; + return status; } -/* virtual */ void -PxrMayaHdShapeAdapter::SetRootXform(const GfMatrix4d& transform) -{ - _rootXform = transform; -} - -/* virtual */ -const SdfPath& -PxrMayaHdShapeAdapter::GetDelegateID() const -{ - return SdfPath::EmptyPath(); -} - -/* virtual */ -const MDagPath& -PxrMayaHdShapeAdapter::GetDagPath() const +PxrMayaHdShapeAdapter::_MarkRenderTasksDirty(HdDirtyBits dirtyBits) { - return _shapeDagPath; -} - -/* virtual */ -TfToken -PxrMayaHdShapeAdapter::_GetRprimCollectionName() const -{ - MStatus status; - const MObject shapeObj = _shapeDagPath.node(&status); - CHECK_MSTATUS_AND_RETURN(status, TfToken()); - const MFnDependencyNode depNodeFn(shapeObj, &status); - CHECK_MSTATUS_AND_RETURN(status, TfToken()); - const MUuid shapeUuid = depNodeFn.uuid(&status); - CHECK_MSTATUS_AND_RETURN(status, TfToken()); - - return TfToken(TfMakeValidIdentifier(shapeUuid.asString().asChar())); + HdRenderIndex* renderIndex = + UsdMayaGLBatchRenderer::GetInstance().GetRenderIndex(); + + for (const auto& iter : _renderTaskIdMap) { + // The render tasks represented by the IDs in this map are instantiated + // lazily, so check that the task exists before attempting to dirty it. + if (renderIndex->HasTask(iter.second)) { + renderIndex->GetChangeTracker().MarkTaskDirty( + iter.second, + dirtyBits); + } + } } /* static */ bool PxrMayaHdShapeAdapter::_GetWireframeColor( - const unsigned int displayStyle, - const MHWRender::DisplayStatus displayStatus, + MHWRender::DisplayStatus displayStatus, const MDagPath& shapeDagPath, - MColor* mayaWireColor) + GfVec4f* wireframeColor) { bool useWireframeColor = false; + MColor mayaWireframeColor; + // Dormant objects may be included in a soft selection. if (displayStatus == MHWRender::kDormant) { auto& batchRenderer = UsdMayaGLBatchRenderer::GetInstance(); if (batchRenderer.GetObjectSoftSelectEnabled()) { const UsdMayaGLSoftSelectHelper& softSelectHelper = UsdMayaGLBatchRenderer::GetInstance().GetSoftSelectHelper(); - useWireframeColor = softSelectHelper.GetFalloffColor(shapeDagPath, - mayaWireColor); + useWireframeColor = + softSelectHelper.GetFalloffColor( + shapeDagPath, + &mayaWireframeColor); } } - // If the object isn't included in a soft selection, just ask Maya for the - // wireframe color. - if (!useWireframeColor && mayaWireColor != nullptr) { - *mayaWireColor = - MHWRender::MGeometryUtilities::wireframeColor(shapeDagPath); - } - - constexpr unsigned int wireframeDisplayStyles = ( - MHWRender::MFrameContext::DisplayStyle::kWireFrame | - MHWRender::MFrameContext::DisplayStyle::kBoundingBox); - - const bool wireframeStyle = (displayStyle & wireframeDisplayStyles); + if (wireframeColor != nullptr) { + // The caller wants a color returned. If the object isn't included in a + // soft selection, just ask Maya for the wireframe color. + if (!useWireframeColor) { + mayaWireframeColor = + MHWRender::MGeometryUtilities::wireframeColor(shapeDagPath); + } - const bool isActive = - (displayStatus == MHWRender::DisplayStatus::kActive) || - (displayStatus == MHWRender::DisplayStatus::kHilite) || - (displayStatus == MHWRender::DisplayStatus::kActiveTemplate) || - (displayStatus == MHWRender::DisplayStatus::kActiveComponent) || - (displayStatus == MHWRender::DisplayStatus::kLead); + *wireframeColor = GfVec4f( + mayaWireframeColor.r, + mayaWireframeColor.g, + mayaWireframeColor.b, + mayaWireframeColor.a); + } - if (wireframeStyle || isActive) { + if (_IsActiveDisplayStatus(displayStatus)) { useWireframeColor = true; } diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/shapeAdapter.h b/lib/mayaUsd/render/pxrUsdMayaGL/shapeAdapter.h index 698b7270f6..ba35b21f7c 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/shapeAdapter.h +++ b/lib/mayaUsd/render/pxrUsdMayaGL/shapeAdapter.h @@ -19,12 +19,16 @@ /// \file pxrUsdMayaGL/shapeAdapter.h #include +#include #include #include +#include #include +#include #include #include +#include #include // XXX: With Maya versions up through 2019 on Linux, M3dView.h ends up @@ -43,7 +47,6 @@ #include #undef Always // Defined in /usr/lib/X11/X.h (eventually included by M3dView.h) - breaks pxr/usd/lib/usdUtils/registeredVariantSet.h #include -#include #include #include #include @@ -66,7 +69,7 @@ class PxrMayaHdShapeAdapter /// Update the shape adapter's state from the shape with the given /// \p shapeDagPath and the legacy viewport display state. MAYAUSD_CORE_PUBLIC - virtual bool Sync( + bool Sync( const MDagPath& shapeDagPath, const M3dView::DisplayStyle legacyDisplayStyle, const M3dView::DisplayStatus legacyDisplayStatus); @@ -74,7 +77,7 @@ class PxrMayaHdShapeAdapter /// Update the shape adapter's state from the shape with the given /// \p shapeDagPath and the Viewport 2.0 display state. MAYAUSD_CORE_PUBLIC - virtual bool Sync( + bool Sync( const MDagPath& shapeDagPath, const unsigned int displayStyle, const MHWRender::DisplayStatus displayStatus); @@ -143,7 +146,7 @@ class PxrMayaHdShapeAdapter const MBoundingBox* boundingBox = nullptr); /// Gets the HdReprSelector that corresponds to the given Maya display - /// state. + /// style. /// /// \p displayStyle should be a bitwise combination of /// MHWRender::MFrameContext::DisplayStyle values, typically either @@ -152,52 +155,87 @@ class PxrMayaHdShapeAdapter /// obtained using MHWRender::MFrameContext::getDisplayStyle() for /// Viewport 2.0. /// - /// \p displayStatus is typically either up-converted from - /// a M3dView::DisplayStatus value obtained using - /// MDrawInfo::displayStatus() for the legacy viewport, or obtained - /// using MHWRender::MGeometryUtilities::displayStatus() for Viewport - /// 2.0. + /// The HdReprSelector chosen is also dependent on the display status + /// (active/selected vs. inactive) which Maya is queried for, as well + /// as whether or not the render params specify that we are using the + /// shape's wireframe, which is influenced by both the display status + /// and whether or not the shape is involved in a soft selection. /// /// If there is no corresponding HdReprSelector for the given display - /// state, an empty HdReprSelector is returned. + /// style, an empty HdReprSelector is returned. MAYAUSD_CORE_PUBLIC - virtual HdReprSelector GetReprSelectorForDisplayState( - const unsigned int displayStyle, - const MHWRender::DisplayStatus displayStatus) const; + HdReprSelector GetReprSelectorForDisplayStyle( + unsigned int displayStyle) const; + + /// Get the render params for the shape adapter's current state. + const PxrMayaHdRenderParams& GetRenderParams() const { + return _renderParams; + } - /// Get a set of render params from the shape adapter's current state. + /// Get the rprim collection for the given repr. /// - /// Sets \p drawShape and \p drawBoundingBox depending on whether shape - /// and/or bounding box rendering is indicated from the state. - MAYAUSD_CORE_PUBLIC - virtual PxrMayaHdRenderParams GetRenderParams( - bool* drawShape, - bool* drawBoundingBox) const; + /// These collections are created when the shape adapter's MDagPath is + /// set to a valid Maya shape. + /// + /// Returns an empty collection if there is no collection for the given + /// repr. + const HdRprimCollection& GetRprimCollection( + const HdReprSelector& repr) const { + const auto& iter = _rprimCollectionMap.find(repr); + if (iter != _rprimCollectionMap.cend()) { + return iter->second; + } + + static const HdRprimCollection emptyCollection; + return emptyCollection; + } - MAYAUSD_CORE_PUBLIC - virtual const HdRprimCollection& GetRprimCollection() const; - - /// Retrieves the render tags for this shape. I.e. which - /// prim purposes should be drawn (such as geomerty, proxy, guides - /// and/or render). - /// This function just returns the _renderTags attribute and it - /// is expected each subclass update the attribute in _Sync() or - /// overrides this function if it needs special processing. - MAYAUSD_CORE_PUBLIC - virtual const TfTokenVector& GetRenderTags() const; + /// Get the ID of the render task for the collection of the given repr. + /// + /// These render task IDs are created when the shape adapter's MDagPath + /// is set to a valid Maya shape. + /// + /// Returns an empty SdfPath if there is no render task ID for the + /// given repr. + const SdfPath& GetRenderTaskId(const HdReprSelector& repr) const { + const auto& iter = _renderTaskIdMap.find(repr); + if (iter != _renderTaskIdMap.cend()) { + return iter->second; + } + + static const SdfPath emptyTaskId; + return emptyTaskId; + } + /// Retrieves the render tags for this shape (i.e. which prim purposes + /// should be drawn, such as geometry, proxy, guide and/or render). + /// + /// This function just returns the _renderTags attribute and it is + /// expected that subclasses update the attribute in _Sync(). + const TfTokenVector& GetRenderTags() const { + return _renderTags; + } - MAYAUSD_CORE_PUBLIC - virtual const GfMatrix4d& GetRootXform() const; + const GfMatrix4d& GetRootXform() const { + return _rootXform; + } - MAYAUSD_CORE_PUBLIC - virtual void SetRootXform(const GfMatrix4d& transform); + /// Sets the root transform for the shape adapter. + /// + /// This function is virtual in case the shape adapter needs to update + /// other state in response to a change in the root transform (e.g. + /// updating an HdSceneDelegate). + virtual void SetRootXform(const GfMatrix4d& transform) { + _rootXform = transform; + } - MAYAUSD_CORE_PUBLIC - virtual const SdfPath& GetDelegateID() const; + const SdfPath& GetDelegateID() const { + return _delegateId; + } - MAYAUSD_CORE_PUBLIC - virtual const MDagPath& GetDagPath() const; + const MDagPath& GetDagPath() const { + return _shapeDagPath; + } /// Get whether this shape adapter is for use with Viewport 2.0. /// @@ -227,19 +265,31 @@ class PxrMayaHdShapeAdapter const unsigned int displayStyle, const MHWRender::DisplayStatus displayStatus) = 0; - /// Helper for computing the name of the shape's HdRprimCollection. + /// Sets the shape adapter's DAG path. /// - /// The batch renderer currently creates a render task for each shape's - /// HdRprimCollection, and those render tasks are identified by an + /// This re-computes the "identifier" for the shape, which is used to + /// compute the names of the shape's HdRprimCollections. The batch + /// renderer will create a render task for each of the shape's + /// HdRprimCollections, and those render tasks are identified by an /// SdfPath constructed using the collection's name. We therefore need - /// the collection to have a name that is unique to the shape it - /// represents and also sanitized for use in SdfPaths. + /// the collections to have names that are unique to the shape they + /// represent and also sanitized for use in SdfPaths. + /// + /// The identifier will be a TfToken that is unique to the shape and is + /// a valid SdfPath identifier, or an empty TfToken if there is an + /// error, which can be detected from the returned MStatus. + MAYAUSD_CORE_PUBLIC + MStatus _SetDagPath(const MDagPath& dagPath); + + /// Mark the render tasks for this shape dirty. /// - /// Returns a TfToken collection name that is unique to the shape and - /// is a valid SdfPath identifier, or an empty TfToken is there is an - /// error. + /// The batch renderer currently creates a render task for each shape's + /// HdRprimCollection, but it has no knowledge of when the collection + /// is changed, for example. This method should be called to notify the + /// batch renderer when such a change is made. MAYAUSD_CORE_PUBLIC - virtual TfToken _GetRprimCollectionName() const; + void _MarkRenderTasksDirty( + HdDirtyBits dirtyBits = HdChangeTracker::AllDirty); /// Helper for getting the wireframe color of the shape. /// @@ -249,18 +299,26 @@ class PxrMayaHdShapeAdapter /// access the soft selection info. /// /// Returns true if the wireframe color should be used, that is if the - /// object and/or its component(s) are involved in a selection, or if - /// the displayStyle indicates that a wireframe style is being drawn - /// (either kWireFrame or kBoundingBox). Otherwise returns false. + /// object and/or its component(s) are involved in a selection. + /// Otherwise returns false. /// - /// The wireframe color will always be returned in \p mayaWireColor (if - /// it is not nullptr) in case the caller wants to use other criteria - /// for determining whether to use it. + /// Note that we do not factor in the viewport's displayStyle, which + /// may indicate that a wireframe style is being drawn (either + /// kWireFrame or kBoundingBox). The displayStyle can be changed + /// without triggering a re-Sync(), so we want to make sure that shape + /// adapters don't inadvertently "bake in" whether to use the wireframe + /// into their render params based on it. We only want to know whether + /// we need to use the wireframe for a reason *other* than the + /// displayStyle. + /// + /// The wireframe color will always be returned in \p wireframeColor + /// (if it is not nullptr) in case the caller wants to use other + /// criteria for determining whether to use it (e.g. for bounding + /// boxes). static bool _GetWireframeColor( - const unsigned int displayStyle, - const MHWRender::DisplayStatus displayStatus, + MHWRender::DisplayStatus displayStatus, const MDagPath& shapeDagPath, - MColor* mayaWireColor); + GfVec4f* wireframeColor); /// Helper for computing the viewport visibility of the shape. /// @@ -283,18 +341,46 @@ class PxrMayaHdShapeAdapter MAYAUSD_CORE_PUBLIC virtual ~PxrMayaHdShapeAdapter(); - MDagPath _shapeDagPath; + TfToken _shapeIdentifier; + SdfPath _delegateId; PxrMayaHdRenderParams _renderParams; - bool _drawShape; - bool _drawBoundingBox; - HdRprimCollection _rprimCollection; - TfTokenVector _renderTags; + struct _ReprHashFunctor { + size_t operator()(const HdReprSelector& repr) const { + // Since we currently only use the refinedToken of + // HdReprSelector, we only need to consider that one token when + // hashing. + return repr[0u].Hash(); + } + }; + + // Mapping of HdReprSelector to the rprim collection for that selector. + using _RprimCollectionMap = + std::unordered_map< + const HdReprSelector, + const HdRprimCollection, + _ReprHashFunctor>; + _RprimCollectionMap _rprimCollectionMap; + + // Mapping of HdReprSelector to the ID of the render task for the + // collection for that selector. + using _RenderTaskIdMap = + std::unordered_map< + const HdReprSelector, + const SdfPath, + _ReprHashFunctor>; + _RenderTaskIdMap _renderTaskIdMap; + + TfTokenVector _renderTags; GfMatrix4d _rootXform; const bool _isViewport2; + + private: + + MDagPath _shapeDagPath; }; diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/softSelectHelper.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/softSelectHelper.cpp index 2b6d1f29e5..a951761cb8 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/softSelectHelper.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/softSelectHelper.cpp @@ -53,7 +53,7 @@ UsdMayaGLSoftSelectHelper::Populate() _populated = true; } -void +void UsdMayaGLSoftSelectHelper::_PopulateWeights() { // we don't want to fallback to the active selection if there is no sot diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/softSelectHelper.h b/lib/mayaUsd/render/pxrUsdMayaGL/softSelectHelper.h index 2d90f14987..930eb1ef9e 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/softSelectHelper.h +++ b/lib/mayaUsd/render/pxrUsdMayaGL/softSelectHelper.h @@ -44,7 +44,7 @@ PXR_NAMESPACE_OPEN_SCOPE /// While this class doesn't have anything particular to rendering, it is only /// used by the render and is therefore here. We can move this to usdMaya if /// we'd like to use it outside of the rendering. -class UsdMayaGLSoftSelectHelper +class UsdMayaGLSoftSelectHelper { public: MAYAUSD_CORE_PUBLIC @@ -59,7 +59,7 @@ class UsdMayaGLSoftSelectHelper void Populate(); /// \brief Returns true if \p dagPath is in the softSelection. Also returns - /// the \p weight. + /// the \p weight. /// /// NOTE: until MAYA-73448 (and MAYA-73513) is fixed, the \p weight value is /// arbitrary. diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.cpp index 0ba420b78c..dde5db18f2 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.cpp @@ -17,16 +17,11 @@ #include -#include - #include -#include #include -#include #include #include #include -#include #include #include #include @@ -34,7 +29,6 @@ #include #include -#include #include #include #include @@ -43,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -75,7 +68,7 @@ PxrMayaHdUsdProxyShapeAdapter::UpdateVisibility(const M3dView* view) // USD proxy shapes are being filtered from this view, so don't bother // checking any other visibility state. isVisible = false; - } else if (!_GetVisibility(_shapeDagPath, view, &isVisible)) { + } else if (!_GetVisibility(GetDagPath(), view, &isVisible)) { return false; } @@ -105,17 +98,6 @@ PxrMayaHdUsdProxyShapeAdapter::SetRootXform(const GfMatrix4d& transform) } } -/* virtual */ -const SdfPath& -PxrMayaHdUsdProxyShapeAdapter::GetDelegateID() const -{ - if (_delegate) { - return _delegate->GetDelegateID(); - } - - return SdfPath::EmptyPath(); -} - /* virtual */ bool PxrMayaHdUsdProxyShapeAdapter::_Sync( @@ -130,7 +112,7 @@ PxrMayaHdUsdProxyShapeAdapter::_Sync( MProfiler::kColorE_L2, "USD Proxy Shape Syncing Shape Adapter"); - MayaUsdProxyShapeBase* usdProxyShape = + MayaUsdProxyShapeBase* usdProxyShape = MayaUsdProxyShapeBase::GetShapeAtDagPath(shapeDagPath); if (!usdProxyShape) { TF_DEBUG(PXRUSDMAYAGL_SHAPE_ADAPTER_LIFECYCLE).Msg( @@ -163,12 +145,12 @@ PxrMayaHdUsdProxyShapeAdapter::_Sync( // require us to re-initialize the shape adapter. HdRenderIndex* renderIndex = UsdMayaGLBatchRenderer::GetInstance().GetRenderIndex(); - if (!(shapeDagPath == _shapeDagPath) || + if (!(shapeDagPath == GetDagPath()) || usdPrim != _rootPrim || excludedPrimPaths != _excludedPrimPaths || !_delegate || renderIndex != &_delegate->GetRenderIndex()) { - _shapeDagPath = shapeDagPath; + _SetDagPath(shapeDagPath); _rootPrim = usdPrim; _excludedPrimPaths = excludedPrimPaths; @@ -208,7 +190,7 @@ PxrMayaHdUsdProxyShapeAdapter::_Sync( #endif MStatus status; - const MMatrix transform = _shapeDagPath.inclusiveMatrix(&status); + const MMatrix transform = GetDagPath().inclusiveMatrix(&status); if (status == MS::kSuccess) { _rootXform = GfMatrix4d(transform.matrix); _delegate->SetRootTransform(_rootXform); @@ -219,60 +201,28 @@ PxrMayaHdUsdProxyShapeAdapter::_Sync( // Will only react if time actually changes. _delegate->SetTime(timeCode); - unsigned int reprDisplayStyle = displayStyle; - - MColor mayaWireframeColor; - const bool useWireframeColor = + _renderParams.useWireframe = _GetWireframeColor( - displayStyle, displayStatus, - _shapeDagPath, - &mayaWireframeColor); - if (useWireframeColor) { - _renderParams.wireframeColor = GfVec4f(mayaWireframeColor.r, - mayaWireframeColor.g, - mayaWireframeColor.b, - mayaWireframeColor.a); - - // Add in kWireFrame to the display style we'll use to determine the - // repr selector (e.g. so that we draw the wireframe over the shaded - // geometry for selected objects). - reprDisplayStyle |= MHWRender::MFrameContext::DisplayStyle::kWireFrame; - } - - HdReprSelector reprSelector = - GetReprSelectorForDisplayState( - reprDisplayStyle, - displayStatus); - - _drawShape = reprSelector.AnyActiveRepr(); - _drawBoundingBox = - (displayStyle & MHWRender::MFrameContext::DisplayStyle::kBoundingBox); - + GetDagPath(), + &_renderParams.wireframeColor); + + // XXX: This is not technically correct. Since the display style can vary + // per viewport, this decision of whether or not to enable lighting should + // be delayed until when the repr for each viewport is known during batched + // drawing. For now, the incorrectly shaded wireframe is not too offensive + // though. + // // If the repr selector specifies a wireframe-only repr, then disable - // lighting. + // lighting. The useWireframe property of the render params is used to + // determine the repr, so be sure to do this *after* that has been set. + const HdReprSelector reprSelector = + GetReprSelectorForDisplayStyle(displayStyle); if (reprSelector.Contains(HdReprTokens->wire) || reprSelector.Contains(HdReprTokens->refinedWire)) { _renderParams.enableLighting = false; } - if (_delegate->GetRootVisibility() != _drawShape) { - _delegate->SetRootVisibility(_drawShape); - } - - if (_rprimCollection.GetReprSelector() != reprSelector) { - _rprimCollection.SetReprSelector(reprSelector); - - TF_DEBUG(PXRUSDMAYAGL_SHAPE_ADAPTER_LIFECYCLE).Msg( - " Repr selector changed: %s\n" - " Marking collection dirty: %s\n", - reprSelector.GetText(), - _rprimCollection.GetName().GetText()); - - _delegate->GetRenderIndex().GetChangeTracker().MarkCollectionDirty( - _rprimCollection.GetName()); - } - HdCullStyle cullStyle = HdCullStyleNothing; if (displayStyle & MHWRender::MFrameContext::DisplayStyle::kBackfaceCulling) { cullStyle = HdCullStyleBackUnlessDoubleSided; @@ -293,65 +243,26 @@ PxrMayaHdUsdProxyShapeAdapter::_Init(HdRenderIndex* renderIndex) MProfiler::kColorE_L2, "USD Proxy Shape Initializing Shape Adapter"); - if (!TF_VERIFY(renderIndex, - "Cannot initialize shape adapter with invalid HdRenderIndex")) { + if (!TF_VERIFY( + renderIndex, + "Cannot initialize shape adapter with invalid HdRenderIndex")) { return false; } - const SdfPath delegatePrefix = - UsdMayaGLBatchRenderer::GetInstance().GetDelegatePrefix(_isViewport2); - - // Create a simple "name" for this shape adapter to insert into the batch - // renderer's SdfPath hierarchy. - // - // XXX: For as long as we're using the MAYA_VP2_USE_VP1_SELECTION - // environment variable, we need to be able to pass responsibility back and - // forth between the MPxDrawOverride's shape adapter for drawing and the - // MPxSurfaceShapeUI's shape adapter for selection. This requires both - // shape adapters to have the same "name", which forces us to build it - // from data on the shape that will be common to both classes, as we do - // below. When we remove MAYA_VP2_USE_VP1_SELECTION and can trust that a - // single shape adapter handles both drawing and selection, we can do - // something even simpler instead like using the shape adapter's memory - // address as the "name". - size_t shapeHash(MObjectHandle(_shapeDagPath.transform()).hashCode()); - boost::hash_combine(shapeHash, _rootPrim); - boost::hash_combine(shapeHash, _excludedPrimPaths); - - // We prepend the Maya type name to the beginning of the delegate name to - // ensure that there are no name collisions between shape adapters of - // shapes with different Maya types. - const TfToken delegateName( - TfStringPrintf("%s_%zx", - MayaUsdProxyShapeBaseTokens->MayaTypeName.GetText(), - shapeHash)); - - const SdfPath delegateId = delegatePrefix.AppendChild(delegateName); - - if (_delegate && - delegateId == GetDelegateID() && - renderIndex == &_delegate->GetRenderIndex()) { - // The delegate's current ID matches the delegate ID we computed and - // the render index matches, so it must be up to date already. - return true; - } - - const TfToken collectionName = _GetRprimCollectionName(); - TF_DEBUG(PXRUSDMAYAGL_SHAPE_ADAPTER_LIFECYCLE).Msg( "Initializing PxrMayaHdUsdProxyShapeAdapter: %p\n" - " shape DAG path : %s\n" - " collection name: %s\n" - " delegateId : %s\n", + " shape DAG path : %s\n" + " shape identifier: %s\n" + " delegateId : %s\n", this, - _shapeDagPath.fullPathName().asChar(), - collectionName.GetText(), - delegateId.GetText()); + GetDagPath().fullPathName().asChar(), + _shapeIdentifier.GetText(), + _delegateId.GetText()); - _delegate.reset(new UsdImagingDelegate(renderIndex, delegateId)); + _delegate.reset(new UsdImagingDelegate(renderIndex, _delegateId)); if (!TF_VERIFY(_delegate, "Failed to create shape adapter delegate for shape %s", - _shapeDagPath.fullPathName().asChar())) { + GetDagPath().fullPathName().asChar())) { return false; } @@ -371,14 +282,6 @@ PxrMayaHdUsdProxyShapeAdapter::_Init(HdRenderIndex* renderIndex) _delegate->Populate(_rootPrim, _excludedPrimPaths, SdfPathVector()); - if (collectionName != _rprimCollection.GetName()) { - _rprimCollection.SetName(collectionName); - renderIndex->GetChangeTracker().AddCollection(_rprimCollection.GetName()); - } - - _rprimCollection.SetReprSelector(HdReprSelector(HdReprTokens->refined)); - _rprimCollection.SetRootPath(delegateId); - return true; } diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.h b/lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.h index 70cb70f643..58f5395138 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.h +++ b/lib/mayaUsd/render/pxrUsdMayaGL/usdProxyShapeAdapter.h @@ -80,16 +80,13 @@ class PxrMayaHdUsdProxyShapeAdapter : public PxrMayaHdShapeAdapter /// Gets whether the shape adapter's shape is visible. /// /// This should be called after a call to UpdateVisibility() to ensure - /// that the returned value is correct. + /// that the returned value is correct. MAYAUSD_CORE_PUBLIC bool IsVisible() const override; MAYAUSD_CORE_PUBLIC void SetRootXform(const GfMatrix4d& transform) override; - MAYAUSD_CORE_PUBLIC - const SdfPath& GetDelegateID() const override; - protected: /// Update the shape adapter's state from the shape with the given @@ -121,9 +118,10 @@ class PxrMayaHdUsdProxyShapeAdapter : public PxrMayaHdShapeAdapter /// Initialize the shape adapter using the given \p renderIndex. /// /// This method is called automatically during Sync() when the shape - /// adapter's "identity" changes. This happens when the delegateId or - /// the rprim collection name computed from the shape adapter's shape - /// is different than what is currently stored in the shape adapter. + /// adapter's "identity" changes. This can happen when the shape + /// managed by this adapter is changed by setting a new DAG path, or + /// otherwise when there is some other fundamental change to the shape + /// or to the delegate or render index. /// The shape adapter will then query the batch renderer for its render /// index and use that to re-create its delegate and re-add its rprim /// collection, if necessary. diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/userData.cpp b/lib/mayaUsd/render/pxrUsdMayaGL/userData.cpp index 740410d767..fd63f76e44 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/userData.cpp +++ b/lib/mayaUsd/render/pxrUsdMayaGL/userData.cpp @@ -24,12 +24,8 @@ PXR_NAMESPACE_OPEN_SCOPE /// Note that we set deleteAfterUse=false when calling the MUserData /// constructor. This ensures that the draw data survives across multiple draw /// passes in Viewport 2.0 (e.g. a shadow pass and a color pass). -/// -/// drawShape is initialized to false, since we expect that the -/// pxrHdImagingShape will be the only one to set it to true. PxrMayaHdUserData::PxrMayaHdUserData() : - MUserData(/* deleteAfterUse = */ false), - drawShape(false) + MUserData(/* deleteAfterUse = */ false) { } diff --git a/lib/mayaUsd/render/pxrUsdMayaGL/userData.h b/lib/mayaUsd/render/pxrUsdMayaGL/userData.h index d2b05f42cb..db20d58d05 100644 --- a/lib/mayaUsd/render/pxrUsdMayaGL/userData.h +++ b/lib/mayaUsd/render/pxrUsdMayaGL/userData.h @@ -41,7 +41,6 @@ class PxrMayaHdUserData : public MUserData { public: - bool drawShape; std::unique_ptr boundingBox; std::unique_ptr wireframeColor;