diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp index 3e43bdf37f..01d4f003a2 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,63 @@ static const std::size_t kRefreshDuration { 1000 }; namespace { +// USD `UsdImagingDelegate::ApplyPendingUpdates()` would request to +// remove the material then recreate, this is causing texture disappearing +// when user manipulating a prim (while holding mouse buttion). +// We hold a copy of the texture info reference, so that the texture will not +// get released immediately along with material removal. +// If the textures would have been requested to reload in `ApplyPendingUpdates()`, +// we could still reuse the loaded one from cache, otherwise the idle task can +// safely release the texture. +class _TransientTexturePreserver +{ +public: + static _TransientTexturePreserver& GetInstance() + { + static _TransientTexturePreserver sInstance; + return sInstance; + } + + void PreserveTextures(HdVP2LocalTextureMap& localTextureMap) + { + if (_isExiting) { + return; + } + + // Locking to avoid race condition for insertion to pendingRemovalTextures + std::lock_guard lock(_removalTaskMutex); + + // Avoid creating multiple idle tasks if there is already one + bool hasRemovalTask = !_pendingRemovalTextures.empty(); + for (const auto& info : localTextureMap) { + _pendingRemovalTextures.emplace(info.second); + } + + if (!hasRemovalTask) { + // Note that we do not need locking inside idle task since it will + // only be executed serially. + MGlobal::executeTaskOnIdle( + [](void* data) { _TransientTexturePreserver::GetInstance().Clear(); }); + } + } + + void Clear() { _pendingRemovalTextures.clear(); } + + void OnMayaExit() + { + _isExiting = true; + Clear(); + } + +private: + _TransientTexturePreserver() = default; + ~_TransientTexturePreserver() = default; + + std::unordered_set _pendingRemovalTextures; + std::mutex _removalTaskMutex; + bool _isExiting = false; +}; + // clang-format off TF_DEFINE_PRIVATE_TOKENS( _tokens, @@ -1734,32 +1792,8 @@ HdVP2Material::~HdVP2Material() // Tell pending tasks or running tasks (if any) to terminate ClearPendingTasks(); - // USD `UsdImagingDelegate::ApplyPendingUpdates()` would request to - // remove the material then recreate, this is causing texture disappearing - // when user manipulating a prim (while holding mouse buttion). - // We hold a copy of the texture info reference, so that the texture will not - // get released immediately along with material removal. - // If the textures would have been requested to reload in `ApplyPendingUpdates()`, - // we could still reuse the loaded one from cache, otherwise the idle task can - // safely release the texture. - static std::unordered_set pendingRemovalTextures; - static std::mutex removalTaskMutex; - if (!_IsDisabledAsyncTextureLoading() && !_localTextureMap.empty()) { - // Locking to avoid race condition for insertion to pendingRemovalTextures - std::lock_guard lock(removalTaskMutex); - - // Avoid creating multiple idle tasks if there is already one - bool hasRemovalTask = !pendingRemovalTextures.empty(); - for (const auto& info : _localTextureMap) { - pendingRemovalTextures.emplace(info.second); - } - - if (!hasRemovalTask) { - // Note that we do not need locking inside idle task since it will - // only be executed serially. - MGlobal::executeTaskOnIdle([](void* data) { pendingRemovalTextures.clear(); }); - } + _TransientTexturePreserver::GetInstance().PreserveTextures(_localTextureMap); } } @@ -3027,6 +3061,19 @@ void HdVP2Material::_UpdateShaderInstance( } } +namespace { + +void exitingCallback(void* /* unusedData */) +{ + // Maya does not unload plugins on exit. Make sure we perform an orderly + // cleanup of the texture cache. Otherwise the cache will clear after VP2 + // has shut down. + HdVP2Material::OnMayaExit(); +} + +MCallbackId gExitingCbId = 0; +} // namespace + /*! \brief Acquires a texture for the given image path. */ const HdVP2TextureInfo& HdVP2Material::_AcquireTexture( @@ -3034,6 +3081,11 @@ const HdVP2TextureInfo& HdVP2Material::_AcquireTexture( const std::string& path, const HdMaterialNode& node) { + // Register for the Maya exit message + if (!gExitingCbId) { + gExitingCbId = MSceneMessage::addCallback(MSceneMessage::kMayaExiting, exitingCallback); + } + // see if we already have the texture loaded. const auto it = _globalTextureMap.find(path); if (it != _globalTextureMap.end()) { @@ -3226,4 +3278,11 @@ void HdVP2Material::_MaterialChanged(HdSceneDelegate* sceneDelegate) #endif +void HdVP2Material::OnMayaExit() +{ + _TransientTexturePreserver::GetInstance().OnMayaExit(); + _globalTextureMap.clear(); + HdVP2RenderDelegate::OnMayaExit(); +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.h b/lib/mayaUsd/render/vp2RenderDelegate/material.h index c52d0f1d91..d640606d9a 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.h +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.h @@ -120,6 +120,8 @@ class HdVP2Material final : public HdMaterial class TextureLoadingTask; friend class TextureLoadingTask; + static void OnMayaExit(); + private: void _ApplyVP2Fixes(HdMaterialNetwork& outNet, const HdMaterialNetwork& inNet); #ifdef WANT_MATERIALX_BUILD diff --git a/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp b/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp index 04c9a865e1..73de8b6624 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp @@ -461,6 +461,23 @@ class MShaderCache final } #endif + void OnMayaExit() + { + if (_isInitialized) { + for (size_t i = 0; i < FallbackShaderTypeCount; ++i) { + _fallbackShaders[i]._map.clear(); + _fallbackCPVShaders[i] = nullptr; + } + _3dSolidShaders._map.clear(); + _3dFatPointShaders._map.clear(); + _userCache._map.clear(); + _3dDefaultMaterialShader = nullptr; + _3dCPVSolidShader = nullptr; + _3dCPVFatPointShader = nullptr; + } + _isInitialized = false; + } + private: bool _isInitialized { false }; //!< Whether the shader cache is initialized @@ -588,6 +605,10 @@ HdVP2RenderDelegate::HdVP2RenderDelegate(ProxyRenderDelegate& drawScene) */ HdVP2RenderDelegate::~HdVP2RenderDelegate() { + CleanupMaterials(); + // Release Textures and Shader resources: + _materialSprims.clear(); + std::lock_guard guard(_renderDelegateMutex); if (_renderDelegateCounter.fetch_sub(1) == 1) { _resourceRegistry.reset(); @@ -1141,4 +1162,6 @@ void HdVP2RenderDelegate::CleanupMaterials() } } +void HdVP2RenderDelegate::OnMayaExit() { sShaderCache.OnMayaExit(); } + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h b/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h index 1fe4b64714..d677a9b02c 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h +++ b/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h @@ -167,6 +167,8 @@ class HdVP2RenderDelegate final : public HdRenderDelegate static const int sProfilerCategory; //!< Profiler category + static void OnMayaExit(); + private: HdVP2RenderDelegate(const HdVP2RenderDelegate&) = delete; HdVP2RenderDelegate& operator=(const HdVP2RenderDelegate&) = delete;